@malloydata/malloy 0.0.334 → 0.0.336

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.
Files changed (91) hide show
  1. package/CONTEXT.md +4 -3
  2. package/MALLOY_API.md +129 -0
  3. package/dist/annotation.d.ts +0 -2
  4. package/dist/annotation.js +29 -23
  5. package/dist/api/asynchronous.d.ts +1 -1
  6. package/dist/api/foundation/cache.d.ts +32 -0
  7. package/dist/api/foundation/cache.js +92 -0
  8. package/dist/api/foundation/compile.d.ts +175 -0
  9. package/dist/api/foundation/compile.js +391 -0
  10. package/dist/api/foundation/core.d.ts +493 -0
  11. package/dist/api/foundation/core.js +1247 -0
  12. package/dist/api/foundation/document.d.ts +167 -0
  13. package/dist/api/foundation/document.js +206 -0
  14. package/dist/api/foundation/index.d.ts +10 -0
  15. package/dist/api/foundation/index.js +77 -0
  16. package/dist/api/foundation/readers.d.ts +53 -0
  17. package/dist/api/foundation/readers.js +134 -0
  18. package/dist/api/foundation/result.d.ts +185 -0
  19. package/dist/api/foundation/result.js +704 -0
  20. package/dist/api/foundation/runtime.d.ts +361 -0
  21. package/dist/api/foundation/runtime.js +733 -0
  22. package/dist/api/foundation/types.d.ts +54 -0
  23. package/dist/api/foundation/types.js +7 -0
  24. package/dist/api/foundation/writers.d.ts +42 -0
  25. package/dist/api/foundation/writers.js +230 -0
  26. package/dist/api/util.d.ts +1 -1
  27. package/dist/connection/base_connection.d.ts +5 -0
  28. package/dist/connection/types.d.ts +5 -0
  29. package/dist/dialect/duckdb/duckdb.js +2 -1
  30. package/dist/dialect/snowflake/snowflake.js +7 -1
  31. package/dist/dialect/trino/trino.js +7 -2
  32. package/dist/index.d.ts +6 -3
  33. package/dist/index.js +30 -26
  34. package/dist/lang/ast/error-factory.js +3 -5
  35. package/dist/lang/ast/expressions/expr-count-distinct.js +7 -1
  36. package/dist/lang/ast/expressions/expr-granular-time.js +1 -1
  37. package/dist/lang/ast/expressions/expr-max.js +7 -1
  38. package/dist/lang/ast/expressions/expr-min.js +7 -1
  39. package/dist/lang/ast/expressions/for-range.js +1 -1
  40. package/dist/lang/ast/source-elements/query-source.js +2 -7
  41. package/dist/lang/ast/source-elements/refined-source.js +11 -1
  42. package/dist/lang/ast/source-elements/sql-source.d.ts +1 -1
  43. package/dist/lang/ast/source-elements/sql-source.js +18 -3
  44. package/dist/lang/ast/sql-elements/sql-string.d.ts +2 -2
  45. package/dist/lang/ast/sql-elements/sql-string.js +18 -1
  46. package/dist/lang/ast/statements/define-source.js +7 -2
  47. package/dist/lang/ast/statements/import-statement.js +53 -21
  48. package/dist/lang/ast/types/document-compile-result.d.ts +1 -0
  49. package/dist/lang/ast/types/expression-def.js +1 -1
  50. package/dist/lang/ast/types/malloy-element.d.ts +3 -1
  51. package/dist/lang/ast/types/malloy-element.js +23 -7
  52. package/dist/lang/malloy-to-ast.d.ts +1 -1
  53. package/dist/lang/malloy-to-ast.js +1 -1
  54. package/dist/lang/parse-malloy.d.ts +3 -2
  55. package/dist/lang/parse-malloy.js +14 -25
  56. package/dist/lang/test/test-translator.d.ts +9 -2
  57. package/dist/lang/test/test-translator.js +103 -77
  58. package/dist/lang/translate-response.d.ts +1 -0
  59. package/dist/model/constant_expression_compiler.js +6 -7
  60. package/dist/model/index.d.ts +3 -1
  61. package/dist/model/index.js +15 -9
  62. package/dist/model/malloy_types.d.ts +89 -15
  63. package/dist/model/malloy_types.js +12 -0
  64. package/dist/model/persist_utils.d.ts +47 -0
  65. package/dist/model/persist_utils.js +257 -0
  66. package/dist/model/query_model_impl.d.ts +2 -4
  67. package/dist/model/query_model_impl.js +5 -13
  68. package/dist/model/query_node.d.ts +1 -2
  69. package/dist/model/query_node.js +3 -13
  70. package/dist/model/query_query.d.ts +17 -1
  71. package/dist/model/query_query.js +81 -36
  72. package/dist/model/source_def_utils.d.ts +50 -0
  73. package/dist/model/source_def_utils.js +154 -0
  74. package/dist/model/sql_block.d.ts +5 -1
  75. package/dist/model/sql_block.js +29 -4
  76. package/dist/model/sql_compiled.d.ts +29 -0
  77. package/dist/model/sql_compiled.js +102 -0
  78. package/dist/model/stage_writer.d.ts +1 -3
  79. package/dist/model/stage_writer.js +7 -25
  80. package/dist/model/utils.d.ts +20 -1
  81. package/dist/model/utils.js +40 -0
  82. package/dist/run_sql_options.d.ts +0 -1
  83. package/dist/taggable.d.ts +10 -0
  84. package/dist/taggable.js +7 -0
  85. package/dist/version.d.ts +1 -1
  86. package/dist/version.js +1 -1
  87. package/package.json +6 -4
  88. package/dist/malloy.d.ts +0 -1365
  89. package/dist/malloy.js +0 -3421
  90. package/dist/model/materialization/utils.d.ts +0 -3
  91. package/dist/model/materialization/utils.js +0 -41
package/dist/malloy.js DELETED
@@ -1,3421 +0,0 @@
1
- "use strict";
2
- /*
3
- * Copyright 2023 Google LLC
4
- *
5
- * Permission is hereby granted, free of charge, to any person obtaining
6
- * a copy of this software and associated documentation files
7
- * (the "Software"), to deal in the Software without restriction,
8
- * including without limitation the rights to use, copy, modify, merge,
9
- * publish, distribute, sublicense, and/or sell copies of the Software,
10
- * and to permit persons to whom the Software is furnished to do so,
11
- * subject to the following conditions:
12
- *
13
- * The above copyright notice and this permission notice shall be
14
- * included in all copies or substantial portions of the Software.
15
- *
16
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
- */
24
- Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.InMemoryModelCache = exports.CacheManager = exports.CSVWriter = exports.JSONWriter = exports.DataWriter = exports.DataRecord = exports.DataArray = exports.Result = exports.ExploreMaterializer = exports.PreparedResultMaterializer = exports.QueryMaterializer = exports.ModelMaterializer = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.Runtime = exports.ExploreField = exports.JoinRelationship = exports.QueryField = exports.Query = exports.StringField = exports.UnsupportedField = exports.JSONField = exports.BooleanField = exports.NumberField = exports.TimestampField = exports.DateField = exports.TimestampTimeframe = exports.DateTimeframe = exports.AtomicField = exports.AtomicFieldType = exports.Explore = exports.SourceRelationship = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.PreparedResult = exports.DocumentCompletion = exports.DocumentSymbol = exports.DocumentPosition = exports.DocumentRange = exports.DocumentTablePath = exports.Parse = exports.PreparedQuery = exports.Model = exports.MalloyError = exports.Malloy = void 0;
26
- const lang_1 = require("./lang");
27
- const model_1 = require("./model");
28
- const dialect_1 = require("./dialect");
29
- const version_1 = require("./version");
30
- const uuid_1 = require("uuid");
31
- const annotation_1 = require("./annotation");
32
- const sql_block_1 = require("./model/sql_block");
33
- const utils_1 = require("./lang/utils");
34
- const reference_list_1 = require("./lang/reference-list");
35
- const row_data_utils_1 = require("./api/row_data_utils");
36
- function isSourceComponent(source) {
37
- return (source.type === 'table' ||
38
- source.type === 'sql_select' ||
39
- source.type === 'query_source');
40
- }
41
- const MALLOY_INTERNAL_URL = 'internal://internal.malloy';
42
- class Malloy {
43
- static get version() {
44
- return version_1.MALLOY_VERSION;
45
- }
46
- static _parse(source, url, eventStream, options, invalidationKey) {
47
- if (url === undefined) {
48
- url = new URL(MALLOY_INTERNAL_URL);
49
- }
50
- let importBaseURL = url;
51
- if (options === null || options === void 0 ? void 0 : options.importBaseURL) {
52
- importBaseURL = options === null || options === void 0 ? void 0 : options.importBaseURL;
53
- }
54
- const translator = new lang_1.MalloyTranslator(url.toString(), importBaseURL.toString(), {
55
- urls: { [url.toString()]: source },
56
- }, eventStream);
57
- if (options === null || options === void 0 ? void 0 : options.testEnvironment) {
58
- translator.allDialectsEnabled = true;
59
- }
60
- return new Parse(translator, invalidationKey);
61
- }
62
- static parse({ url, urlReader, source, eventStream, options, }) {
63
- if (source !== undefined) {
64
- return Malloy._parse(source, url, eventStream, options);
65
- }
66
- else {
67
- if (urlReader === undefined) {
68
- throw new Error('Internal Error: urlReader is required.');
69
- }
70
- if (url === undefined) {
71
- throw new Error('Internal Error: url is required if source not present.');
72
- }
73
- return readURL(urlReader, url).then(({ contents, invalidationKey }) => {
74
- return Malloy._parse(contents, url, eventStream, options, invalidationKey);
75
- });
76
- }
77
- }
78
- /**
79
- * Compile a parsed Malloy document.
80
- *
81
- * @param urlReader Object capable of reading contents of a URL.
82
- * @param connections Mapping of connection names to objects capable of reading Malloy schemas.
83
- * @param parse The parsed Malloy document.
84
- * @param model A compiled model to build upon (optional).
85
- * @return A (promise of a) compiled `Model`.
86
- */
87
- static async compile({ url, source, parse, urlReader, connections, model, refreshSchemaCache, noThrowOnError, eventStream, importBaseURL, cacheManager, }) {
88
- var _a, _b, _c, _d, _e;
89
- let refreshTimestamp;
90
- if (refreshSchemaCache) {
91
- refreshTimestamp =
92
- typeof refreshSchemaCache === 'number'
93
- ? refreshSchemaCache
94
- : Date.now();
95
- }
96
- if (url === undefined && source === undefined && parse === undefined) {
97
- throw new Error('Internal Error: url, source, or parse required.');
98
- }
99
- if (url === undefined) {
100
- if (parse !== undefined) {
101
- url = new URL(parse._translator.sourceURL);
102
- }
103
- else {
104
- url = new URL(MALLOY_INTERNAL_URL);
105
- }
106
- }
107
- const invalidationKeys = {};
108
- // Before anything, if we have a URL and not source code, we check if that URL
109
- // is cached.
110
- if (source === undefined && cacheManager !== undefined) {
111
- const cached = await cacheManager.getCachedModelDef(urlReader, url.toString());
112
- if (cached) {
113
- return new Model(cached.modelDef, [], // TODO when using a model from cache, should we also store the problems??
114
- [url.toString(), ...flatDeps(cached.modelDef.dependencies)]);
115
- }
116
- }
117
- importBaseURL !== null && importBaseURL !== void 0 ? importBaseURL : (importBaseURL = url);
118
- let translator;
119
- // It's not cached, so we may need to get the actual source
120
- const _url = url.toString();
121
- if (parse !== undefined) {
122
- translator = parse._translator;
123
- const invalidationKey = (_a = parse._invalidationKey) !== null && _a !== void 0 ? _a : (await getInvalidationKey(urlReader, url));
124
- invalidationKeys[_url] = invalidationKey;
125
- }
126
- else {
127
- if (source === undefined) {
128
- const { contents, invalidationKey } = await readURL(urlReader, url);
129
- invalidationKeys[_url] = invalidationKey;
130
- source = contents;
131
- }
132
- else {
133
- const invalidationKey = await getInvalidationKey(urlReader, url);
134
- invalidationKeys[_url] = invalidationKey;
135
- }
136
- translator = new lang_1.MalloyTranslator(_url, importBaseURL.toString(), {
137
- urls: { [_url]: source },
138
- }, eventStream);
139
- }
140
- for (;;) {
141
- const result = translator.translate(model === null || model === void 0 ? void 0 : model._modelDef);
142
- if (result.final) {
143
- if (result.modelDef) {
144
- await (cacheManager === null || cacheManager === void 0 ? void 0 : cacheManager.setCachedModelDef(url.toString(), {
145
- modelDef: result.modelDef,
146
- invalidationKeys,
147
- }));
148
- for (const model of translator.newlyTranslatedDependencies()) {
149
- await (cacheManager === null || cacheManager === void 0 ? void 0 : cacheManager.setCachedModelDef(model.url, {
150
- modelDef: model.modelDef,
151
- invalidationKeys,
152
- }));
153
- }
154
- return new Model(result.modelDef, result.problems || [], [
155
- ...((_b = model === null || model === void 0 ? void 0 : model.fromSources) !== null && _b !== void 0 ? _b : []),
156
- ...((_c = result.fromSources) !== null && _c !== void 0 ? _c : []),
157
- ]);
158
- }
159
- else if (noThrowOnError) {
160
- const emptyModel = {
161
- name: 'modelDidNotCompile',
162
- exports: [],
163
- contents: {},
164
- dependencies: {},
165
- queryList: [],
166
- };
167
- const modelFromCompile = (model === null || model === void 0 ? void 0 : model._modelDef) || emptyModel;
168
- return new Model(modelFromCompile, result.problems || [], [
169
- ...((_d = model === null || model === void 0 ? void 0 : model.fromSources) !== null && _d !== void 0 ? _d : []),
170
- ...((_e = result.fromSources) !== null && _e !== void 0 ? _e : []),
171
- ]);
172
- }
173
- else {
174
- const errors = result.problems || [];
175
- const errText = translator.prettyErrors();
176
- throw new MalloyError(`Error(s) compiling model:\n${errText}`, errors);
177
- }
178
- }
179
- else {
180
- // Parse incomplete because some external information is required,
181
- // there might be more than one of these in a single reply ...
182
- if (result.urls) {
183
- for (const neededUrl of result.urls) {
184
- try {
185
- if (isInternalURL(neededUrl)) {
186
- throw new Error('In order to use relative imports, you must compile a file via a URL.');
187
- }
188
- // First, check the cache
189
- if (cacheManager !== undefined) {
190
- const cached = await cacheManager.getCachedModelDef(urlReader, neededUrl);
191
- if (cached) {
192
- for (const dependency in cached.invalidationKeys) {
193
- invalidationKeys[dependency] =
194
- cached.invalidationKeys[dependency];
195
- }
196
- translator.update({
197
- translations: { [neededUrl]: cached.modelDef },
198
- });
199
- continue;
200
- }
201
- }
202
- // Otherwise, fetch the URL contents
203
- const { contents, invalidationKey } = await readURL(urlReader, new URL(neededUrl));
204
- const urls = { [neededUrl]: contents };
205
- invalidationKeys[neededUrl] = invalidationKey;
206
- translator.update({ urls });
207
- }
208
- catch (error) {
209
- translator.update({
210
- errors: { urls: { [neededUrl]: error.message } },
211
- });
212
- }
213
- }
214
- }
215
- const { modelAnnotation } = translator.modelAnnotation(model === null || model === void 0 ? void 0 : model._modelDef);
216
- if (result.tables) {
217
- // collect tables by connection name since there may be multiple connections
218
- const tablesByConnection = new Map();
219
- for (const tableKey in result.tables) {
220
- const { connectionName, tablePath } = result.tables[tableKey];
221
- const connectionToTablesMap = tablesByConnection.get(connectionName);
222
- if (connectionToTablesMap === undefined) {
223
- tablesByConnection.set(connectionName, { [tableKey]: tablePath });
224
- }
225
- else {
226
- connectionToTablesMap[tableKey] = tablePath;
227
- }
228
- }
229
- // iterate over connections, fetching schema for all missing tables
230
- for (const [connectionName, tablePathByKey] of tablesByConnection) {
231
- try {
232
- const connection = await connections.lookupConnection(connectionName);
233
- // TODO detect if the union of `Object.keys(tables)` and `Object.keys(errors)` is not the same
234
- // as `Object.keys(tablePathByKey)`, i.e. that all tables are accounted for. Otherwise
235
- // the translator runs into an infinite loop fetching tables.
236
- const { schemas: tables, errors } = await Malloy.safelyFetchTableSchema(connection, tablePathByKey, {
237
- refreshTimestamp,
238
- modelAnnotation,
239
- });
240
- translator.update({ tables, errors: { tables: errors } });
241
- }
242
- catch (error) {
243
- // There was an exception getting the connection, associate that error
244
- // with all its tables
245
- const tables = {};
246
- const errors = {};
247
- for (const tableKey in tablePathByKey) {
248
- errors[tableKey] = error.toString();
249
- }
250
- translator.update({ tables, errors: { tables: errors } });
251
- }
252
- }
253
- }
254
- if (result.compileSQL) {
255
- // Unlike other requests, these do not come in batches
256
- const toCompile = result.compileSQL;
257
- const connectionName = toCompile.connection;
258
- const key = (0, sql_block_1.sqlKey)(toCompile.connection, toCompile.selectStr);
259
- try {
260
- const conn = await connections.lookupConnection(connectionName);
261
- const resolved = await conn.fetchSchemaForSQLStruct(toCompile, {
262
- refreshTimestamp,
263
- modelAnnotation,
264
- });
265
- if (resolved.error) {
266
- translator.update({
267
- errors: {
268
- compileSQL: {
269
- [key]: resolved.error,
270
- },
271
- },
272
- });
273
- }
274
- if (resolved.structDef) {
275
- translator.update({
276
- compileSQL: { [key]: resolved.structDef },
277
- });
278
- }
279
- }
280
- catch (error) {
281
- const errors = {};
282
- errors[key] = error.toString();
283
- translator.update({ errors: { compileSQL: errors } });
284
- }
285
- }
286
- }
287
- }
288
- }
289
- /**
290
- * A dialect must provide a response for every table, or the translator loop
291
- * will never exit. Because there was a time when this happened, we throw
292
- * instead of looping forever, but the fix is to correct the dialect.
293
- */
294
- static async safelyFetchTableSchema(connection, toFetch, opts) {
295
- const ret = await connection.fetchSchemaForTables(toFetch, opts);
296
- for (const req of Object.keys(toFetch)) {
297
- if (ret.schemas[req] === undefined && ret.errors[req] === undefined) {
298
- throw new Error(`Schema fetch error for ${connection.name}, no response for ${req} from ${connection.dialectName}`);
299
- }
300
- }
301
- return ret;
302
- }
303
- static async run({ connections, preparedResult, sqlStruct, connection, options, }) {
304
- if (!connection) {
305
- if (!connections) {
306
- throw new Error('Internal Error: Connection or LookupConnection<Connection> must be provided.');
307
- }
308
- const connectionName = (sqlStruct === null || sqlStruct === void 0 ? void 0 : sqlStruct.connection) || (preparedResult === null || preparedResult === void 0 ? void 0 : preparedResult.connectionName);
309
- connection = await connections.lookupConnection(connectionName);
310
- }
311
- if (sqlStruct) {
312
- const data = await connection.runSQL(sqlStruct.selectStr);
313
- return new Result({
314
- structs: [sqlStruct],
315
- sql: sqlStruct.selectStr,
316
- result: data.rows,
317
- totalRows: data.totalRows,
318
- runStats: data.runStats,
319
- lastStageName: sqlStruct.name,
320
- // TODO feature-sql-block There is no malloy code...
321
- malloy: '',
322
- connectionName: sqlStruct.connection,
323
- // TODO feature-sql-block There is no source explore...
324
- sourceExplore: '',
325
- sourceFilters: [],
326
- profilingUrl: data.profilingUrl,
327
- }, {
328
- name: 'empty_model',
329
- exports: [],
330
- contents: {},
331
- queryList: [],
332
- dependencies: {},
333
- });
334
- }
335
- else if (preparedResult) {
336
- const result = await connection.runSQL(preparedResult.sql, options);
337
- return new Result({
338
- ...preparedResult._rawQuery,
339
- result: result.rows,
340
- totalRows: result.totalRows,
341
- runStats: result.runStats,
342
- profilingUrl: result.profilingUrl,
343
- }, preparedResult._modelDef);
344
- }
345
- else {
346
- throw new Error('Internal error: sqlStruct or preparedResult must be provided.');
347
- }
348
- }
349
- static async *runStream({ connections, preparedResult, sqlStruct, connection, options, }) {
350
- if (sqlStruct === undefined && preparedResult === undefined) {
351
- throw new Error('Internal error: sqlBlock or preparedResult must be provided.');
352
- }
353
- const connectionName = (sqlStruct === null || sqlStruct === void 0 ? void 0 : sqlStruct.connection) || (preparedResult === null || preparedResult === void 0 ? void 0 : preparedResult.connectionName);
354
- if (connection === undefined) {
355
- if (connections === undefined) {
356
- throw new Error('Internal Error: Connection or LookupConnection<Connection> must be provided.');
357
- }
358
- connection = await connections.lookupConnection(connectionName);
359
- }
360
- // TODO is there a better way to handle this case? Just require StreamingConnections?
361
- if (!connection.canStream()) {
362
- throw new Error(`Connection '${connectionName}' cannot stream results.`);
363
- }
364
- let sql;
365
- let resultExplore;
366
- if (sqlStruct) {
367
- resultExplore = new Explore(sqlStruct);
368
- sql = sqlStruct.selectStr;
369
- }
370
- else if (preparedResult !== undefined) {
371
- resultExplore = preparedResult.resultExplore;
372
- sql = preparedResult.sql;
373
- }
374
- else {
375
- throw new Error('Internal error: sqlStruct or preparedResult must be provided.');
376
- }
377
- let index = 0;
378
- for await (const row of connection.runSQLStream(sql, options)) {
379
- yield new DataRecord(row, index, resultExplore, undefined, undefined);
380
- index += 1;
381
- }
382
- }
383
- static async estimateQueryCost({ connections, preparedResult, sqlStruct, }) {
384
- if (!connections) {
385
- throw new Error('Internal Error: Connection or LookupConnection<Connection> must be provided.');
386
- }
387
- const connectionName = (sqlStruct === null || sqlStruct === void 0 ? void 0 : sqlStruct.connection) || (preparedResult === null || preparedResult === void 0 ? void 0 : preparedResult.connectionName);
388
- const connection = await connections.lookupConnection(connectionName);
389
- if (sqlStruct) {
390
- return await connection.estimateQueryCost(sqlStruct.selectStr);
391
- }
392
- else if (preparedResult) {
393
- return await connection.estimateQueryCost(preparedResult.sql);
394
- }
395
- else {
396
- throw new Error('Internal error: sqlStruct or preparedResult must be provided.');
397
- }
398
- }
399
- }
400
- exports.Malloy = Malloy;
401
- /**
402
- * A Malloy error, which may contain log messages produced during compilation.
403
- */
404
- class MalloyError extends Error {
405
- /**
406
- * An array of log messages produced during compilation.
407
- */
408
- constructor(message, problems = []) {
409
- super(message);
410
- this.problems = problems;
411
- }
412
- }
413
- exports.MalloyError = MalloyError;
414
- /**
415
- * A compiled Malloy document.
416
- */
417
- class Model {
418
- constructor(modelDef, problems, fromSources) {
419
- var _a, _b;
420
- this.modelDef = modelDef;
421
- this.problems = problems;
422
- this.fromSources = fromSources;
423
- this.references = new reference_list_1.ReferenceList((_a = fromSources[0]) !== null && _a !== void 0 ? _a : '', (_b = modelDef.references) !== null && _b !== void 0 ? _b : []);
424
- }
425
- tagParse(spec) {
426
- return (0, annotation_1.annotationToTag)(this.modelDef.annotation, spec);
427
- }
428
- getTaglines(prefix) {
429
- return (0, annotation_1.annotationToTaglines)(this.modelDef.annotation, prefix);
430
- }
431
- /**
432
- * Retrieve a document reference for the token at the given position within
433
- * the document that produced this model.
434
- *
435
- * @param position A position within the document.
436
- * @return A `DocumentReference` at that position if one exists.
437
- */
438
- getReference(position) {
439
- return this.references.find(position);
440
- }
441
- /**
442
- * Retrieve an import for the token at the given position within
443
- * the document that produced this model.
444
- *
445
- * @param position A position within the document.
446
- * @return An `ImportLocation` at that position if one exists.
447
- */
448
- getImport(position) {
449
- var _a;
450
- return (_a = this.modelDef.imports) === null || _a === void 0 ? void 0 : _a.find(i => (0, utils_1.locationContainsPosition)(i.location, position));
451
- }
452
- /**
453
- * Retrieve a prepared query by the name of a query at the top level of the model.
454
- *
455
- * @param queryName Name of the query to retrieve.
456
- * @return A prepared query.
457
- */
458
- getPreparedQueryByName(queryName) {
459
- const query = this.modelDef.contents[queryName];
460
- if ((query === null || query === void 0 ? void 0 : query.type) === 'query') {
461
- return new PreparedQuery(query, this.modelDef, this.problems, queryName);
462
- }
463
- throw new Error('Given query name does not refer to a named query.');
464
- }
465
- /**
466
- * Retrieve a prepared query by the index of an unnamed query at the top level of a model.
467
- *
468
- * @param index The index of the query to retrieve.
469
- * @return A prepared query.
470
- */
471
- getPreparedQueryByIndex(index) {
472
- if (index < 0) {
473
- throw new Error(`Invalid index ${index}.`);
474
- }
475
- else if (index >= this.modelDef.queryList.length) {
476
- throw new Error(`Query index ${index} is out of bounds.`);
477
- }
478
- return new PreparedQuery(this.modelDef.queryList[index], this.modelDef, this.problems);
479
- }
480
- /**
481
- * Retrieve a prepared query for the final unnamed query at the top level of a model.
482
- *
483
- * @return A prepared query.
484
- */
485
- get preparedQuery() {
486
- return this.getPreparedQuery();
487
- }
488
- /**
489
- * Retrieve a prepared query for the final unnamed query at the top level of a model.
490
- *
491
- * @return A prepared query.
492
- */
493
- getPreparedQuery() {
494
- if (this.modelDef.queryList.length === 0) {
495
- throw new Error('Model has no queries.');
496
- }
497
- return new PreparedQuery(this.modelDef.queryList[this.modelDef.queryList.length - 1], this.modelDef, this.problems);
498
- }
499
- /**
500
- * Retrieve an `Explore` from the model by name.
501
- *
502
- * @param name The name of the `Explore` to retrieve.
503
- * @return An `Explore`.
504
- */
505
- getExploreByName(name) {
506
- const struct = this.modelDef.contents[name];
507
- if (struct && (0, model_1.isSourceDef)(struct)) {
508
- return new Explore(struct);
509
- }
510
- throw new Error("'name' is not an explore");
511
- }
512
- /**
513
- * Get an array of `Explore`s contained in the model.
514
- *
515
- * @return An array of `Explore`s contained in the model.
516
- */
517
- get explores() {
518
- return Object.values(this.modelDef.contents)
519
- .filter(model_1.isSourceDef)
520
- .map(structDef => new Explore(structDef));
521
- }
522
- /**
523
- * Get an array of `NamedQuery`s contained in the model.
524
- *
525
- * @return An array of `NamedQuery`s contained in the model.
526
- */
527
- get namedQueries() {
528
- const isNamedQuery = (object) => object.type === 'query';
529
- return Object.values(this.modelDef.contents).filter(isNamedQuery);
530
- }
531
- get exportedExplores() {
532
- return this.explores.filter(explore => this.modelDef.exports.includes(explore.name));
533
- }
534
- get _modelDef() {
535
- return this.modelDef;
536
- }
537
- }
538
- exports.Model = Model;
539
- /**
540
- * A prepared query which has all the necessary information to produce its SQL.
541
- */
542
- class PreparedQuery {
543
- constructor(query, model, problems, name) {
544
- this.problems = problems;
545
- this.name = name;
546
- this._query = query;
547
- this._modelDef = model;
548
- }
549
- tagParse(spec) {
550
- const modelScope = (0, annotation_1.annotationToTag)(this._modelDef.annotation).tag;
551
- spec = (0, annotation_1.addModelScope)(spec, modelScope);
552
- return (0, annotation_1.annotationToTag)(this._query.annotation, spec);
553
- }
554
- getTaglines(prefix) {
555
- return (0, annotation_1.annotationToTaglines)(this._query.annotation, prefix);
556
- }
557
- /**
558
- * Generate the SQL for this query.
559
- *
560
- * @return A fully-prepared query (which contains the generated SQL).
561
- */
562
- get preparedResult() {
563
- return this.getPreparedResult();
564
- }
565
- /**
566
- * Generate the SQL for this query.
567
- *
568
- * @return A fully-prepared query (which contains the generated SQL).
569
- * @param options.eventStream An event stream to use when compiling the SQL
570
- */
571
- getPreparedResult(options) {
572
- const queryModel = new model_1.QueryModel(this._modelDef, options === null || options === void 0 ? void 0 : options.eventStream);
573
- const translatedQuery = queryModel.compileQuery(this._query, options);
574
- return new PreparedResult({
575
- ...translatedQuery,
576
- queryName: this.name || translatedQuery.queryName,
577
- }, this._modelDef);
578
- }
579
- get dialect() {
580
- const sourceRef = this._query.structRef;
581
- const source = typeof sourceRef === 'string'
582
- ? this._modelDef.contents[sourceRef]
583
- : sourceRef;
584
- if (!(0, model_1.isSourceDef)(source)) {
585
- throw new Error('Invalid source for query');
586
- }
587
- return source.dialect;
588
- }
589
- /**
590
- * Get the flattened version of a query -- one that does not have a `pipeHead`.
591
- * @deprecated Because queries can no longer have `pipeHead`s.
592
- */
593
- getFlattenedQuery(_defaultName) {
594
- return this;
595
- }
596
- }
597
- exports.PreparedQuery = PreparedQuery;
598
- /**
599
- * A parsed Malloy document.
600
- */
601
- class Parse {
602
- constructor(translator, invalidationKey) {
603
- this.translator = translator;
604
- this.invalidationKey = invalidationKey;
605
- }
606
- /**
607
- * Retrieve the symbols defined in the parsed document.
608
- *
609
- * These symbols represent any object defined (e.g. `Query`s and `Explore`s)
610
- * in the document.
611
- *
612
- * @return An array of document symbols.
613
- */
614
- get symbols() {
615
- return (this.translator.metadata().symbols || []).map(symbol => new DocumentSymbol(symbol));
616
- }
617
- /**
618
- * Retrieve the full table paths for tables defined in the parsed document.
619
- * Derived tables i.e. a table that extends another table, table from a query
620
- * are not included.
621
- *
622
- * @return An array of document table path info.
623
- */
624
- get tablePathInfo() {
625
- var _a;
626
- const paths = (_a = this.translator.tablePathInfo().pathInfo) !== null && _a !== void 0 ? _a : [];
627
- return paths.map(path => new DocumentTablePath(path));
628
- }
629
- get _translator() {
630
- return this.translator;
631
- }
632
- get _invalidationKey() {
633
- return this.invalidationKey;
634
- }
635
- completions(position) {
636
- return (this.translator.completions(position).completions || []).map(completion => new DocumentCompletion(completion));
637
- }
638
- helpContext(position) {
639
- return this.translator.helpContext(position).helpContext;
640
- }
641
- }
642
- exports.Parse = Parse;
643
- /**
644
- * Path info for a table defined in a Malloy document.
645
- */
646
- class DocumentTablePath {
647
- constructor(tablePath) {
648
- this._range = DocumentRange.fromJSON(tablePath.range);
649
- this._connectionId = tablePath.connectionId;
650
- this._tablePath = tablePath.tablePath;
651
- }
652
- /**
653
- * @return The range of characters in the source Malloy document that defines
654
- * this table.
655
- */
656
- get range() {
657
- return this._range;
658
- }
659
- /** @return The Connection Id for this table. */
660
- get connectionId() {
661
- return this._connectionId;
662
- }
663
- /** @return The full table path. */
664
- get tablePath() {
665
- return this._tablePath;
666
- }
667
- }
668
- exports.DocumentTablePath = DocumentTablePath;
669
- /**
670
- * A range of characters within a Malloy document.
671
- */
672
- class DocumentRange {
673
- constructor(start, end) {
674
- this._start = start;
675
- this._end = end;
676
- }
677
- /**
678
- * @return The position of the first character in the range.
679
- */
680
- get start() {
681
- return this._start;
682
- }
683
- /**
684
- * @return The position of the last character in the range.
685
- */
686
- get end() {
687
- return this._end;
688
- }
689
- /**
690
- * @return This range in JSON format.
691
- */
692
- toJSON() {
693
- return {
694
- start: this.start.toJSON(),
695
- end: this.end.toJSON(),
696
- };
697
- }
698
- /**
699
- * Construct a DocumentRange from JSON.
700
- */
701
- static fromJSON(json) {
702
- return new DocumentRange(new DocumentPosition(json.start.line, json.start.character), new DocumentPosition(json.end.line, json.end.character));
703
- }
704
- }
705
- exports.DocumentRange = DocumentRange;
706
- /**
707
- * A position within a Malloy document.
708
- */
709
- class DocumentPosition {
710
- constructor(line, character) {
711
- this._line = line;
712
- this._character = character;
713
- }
714
- /**
715
- * @return The line number of the position.
716
- */
717
- get line() {
718
- return this._line;
719
- }
720
- /**
721
- * @return The character index on the line `this.getLine()`.
722
- */
723
- get character() {
724
- return this._character;
725
- }
726
- /**
727
- * @return This position in JSON format.
728
- */
729
- toJSON() {
730
- return { line: this.line, character: this.character };
731
- }
732
- }
733
- exports.DocumentPosition = DocumentPosition;
734
- /**
735
- * A symbol defined in a Malloy document.
736
- *
737
- * Represents any object defined (e.g. `Query`s and `Explore`s) in the document.
738
- */
739
- class DocumentSymbol {
740
- constructor(documentSymbol) {
741
- this._range = DocumentRange.fromJSON(documentSymbol.range);
742
- this._lensRange = documentSymbol.lensRange
743
- ? DocumentRange.fromJSON(documentSymbol.lensRange)
744
- : undefined;
745
- this._type = documentSymbol.type;
746
- this._name = documentSymbol.name;
747
- this._children = documentSymbol.children.map(child => new DocumentSymbol(child));
748
- }
749
- /**
750
- * @return The range of characters in the source Malloy document that define this symbol.
751
- */
752
- get range() {
753
- return this._range;
754
- }
755
- /**
756
- * @return The range of characters in the source Malloy document that define this symbol,
757
- * including tags. Note: "block tags" are included if there is exactly one
758
- * definition in the block.
759
- */
760
- get lensRange() {
761
- var _a;
762
- return (_a = this._lensRange) !== null && _a !== void 0 ? _a : this._range;
763
- }
764
- /**
765
- * @return The type of symbol.
766
- *
767
- * Possible values are: `"explore"`, `"query"`, `"field"`, `"turtle"`, `"join"`, or `"unnamed_query"`.
768
- */
769
- get type() {
770
- return this._type;
771
- }
772
- /**
773
- * @return The name of this symbol, e.g. the `Explore` name or `Query` name.
774
- *
775
- * For type `"unnamed_query"`, `getName()` is `"unnamed_query"`.
776
- */
777
- get name() {
778
- return this._name;
779
- }
780
- /**
781
- * @return An array of document symbols defined inside this document symbol,
782
- * e.g. fields in an `Explore`.
783
- */
784
- get children() {
785
- return this._children;
786
- }
787
- }
788
- exports.DocumentSymbol = DocumentSymbol;
789
- class DocumentCompletion {
790
- constructor(completion) {
791
- this.type = completion.type;
792
- this.text = completion.text;
793
- }
794
- }
795
- exports.DocumentCompletion = DocumentCompletion;
796
- /**
797
- * A fully-prepared query containing SQL and metadata required to run the query.
798
- */
799
- class PreparedResult {
800
- constructor(query, modelDef) {
801
- this.modelDef = modelDef;
802
- this.inner = query;
803
- }
804
- static fromJson({ query, modelDef, }) {
805
- // Validate extracted properties (optional, but recommended)
806
- if (!query || !modelDef) {
807
- throw new Error('Missing required properties in JSON data');
808
- }
809
- return new PreparedResult(query, modelDef);
810
- }
811
- tagParse(spec) {
812
- const modelScope = (0, annotation_1.annotationToTag)(this.modelDef.annotation).tag;
813
- spec = (0, annotation_1.addModelScope)(spec, modelScope);
814
- return (0, annotation_1.annotationToTag)(this.inner.annotation, spec);
815
- }
816
- getTaglines(prefix) {
817
- return (0, annotation_1.annotationToTaglines)(this.inner.annotation, prefix);
818
- }
819
- get annotation() {
820
- return this.inner.annotation;
821
- }
822
- get modelAnnotation() {
823
- return this.modelDef.annotation;
824
- }
825
- get modelTag() {
826
- return (0, annotation_1.annotationToTag)(this.modelDef.annotation).tag;
827
- }
828
- /**
829
- * @return The name of the connection this query should be run against.
830
- */
831
- get connectionName() {
832
- return this.inner.connectionName;
833
- }
834
- get _rawQuery() {
835
- return this.inner;
836
- }
837
- get _modelDef() {
838
- return this.modelDef;
839
- }
840
- /**
841
- * @return The SQL that should be run against the SQL runner
842
- * with the connection name `this.getConnectionName()`.
843
- */
844
- get sql() {
845
- return this.inner.sql;
846
- }
847
- get dependenciesToMaterialize() {
848
- return this.inner.dependenciesToMaterialize;
849
- }
850
- get materialization() {
851
- return this.inner.materialization;
852
- }
853
- /**
854
- * @return The `Explore` representing the data that will be returned by running this query.
855
- */
856
- get resultExplore() {
857
- if (this.inner.structs.length === 0) {
858
- throw new Error('Malformed query result.');
859
- }
860
- const explore = this.inner.structs[this.inner.structs.length - 1];
861
- const namedExplore = {
862
- ...explore,
863
- annotation: this.inner.annotation,
864
- name: this.inner.queryName || explore.name,
865
- };
866
- // TODO `sourceExplore` is not fully-implemented yet -- it cannot
867
- // handle cases where the source of the query is something other than
868
- // a named explore.
869
- try {
870
- return new Explore(namedExplore, this.sourceExplore);
871
- }
872
- catch (error) {
873
- return new Explore(namedExplore);
874
- }
875
- }
876
- get sourceExplore() {
877
- const name = this.inner.sourceExplore;
878
- const explore = this.modelDef.contents[name];
879
- if (explore && (0, model_1.isSourceDef)(explore)) {
880
- return new Explore(explore);
881
- }
882
- }
883
- get _sourceExploreName() {
884
- return this.inner.sourceExplore;
885
- }
886
- get _sourceArguments() {
887
- return this.inner.sourceArguments;
888
- }
889
- get _sourceFilters() {
890
- return this.inner.sourceFilters || [];
891
- }
892
- /**
893
- * @return Whether this result has a schema. DDL statements (INSTALL, LOAD,
894
- * CREATE SECRET, etc.) do not return a schema.
895
- */
896
- get hasSchema() {
897
- return this.inner.structs.length > 0;
898
- }
899
- }
900
- exports.PreparedResult = PreparedResult;
901
- /**
902
- * A URL reader which always throws an error when a URL's contents is requested.
903
- *
904
- * Useful for scenarios in which `import` statements are not required.
905
- */
906
- class EmptyURLReader {
907
- async readURL(_url) {
908
- throw new Error('No files.');
909
- }
910
- async getInvalidationKey(_url) {
911
- throw new Error('No files.');
912
- }
913
- }
914
- exports.EmptyURLReader = EmptyURLReader;
915
- /**
916
- * A URL reader backed by an in-memory mapping of URL contents.
917
- */
918
- class InMemoryURLReader {
919
- constructor(files) {
920
- this.files = files;
921
- }
922
- async readURL(url) {
923
- const file = this.files.get(url.toString());
924
- if (file !== undefined) {
925
- return Promise.resolve({
926
- contents: file,
927
- invalidationKey: this.invalidationKey(url, file),
928
- });
929
- }
930
- else {
931
- throw new Error(`File not found '${url}'`);
932
- }
933
- }
934
- async getInvalidationKey(url) {
935
- const file = this.files.get(url.toString());
936
- if (file !== undefined) {
937
- return Promise.resolve(this.invalidationKey(url, file));
938
- }
939
- else {
940
- throw new Error(`File not found '${url}'`);
941
- }
942
- }
943
- invalidationKey(url, contents) {
944
- if (isInternalURL(url.toString())) {
945
- return null;
946
- }
947
- return hashForInvalidationKey(contents);
948
- }
949
- }
950
- exports.InMemoryURLReader = InMemoryURLReader;
951
- /**
952
- * A fixed mapping of connection names to connections.
953
- */
954
- class FixedConnectionMap {
955
- constructor(connections, defaultConnectionName) {
956
- this.connections = connections;
957
- this.defaultConnectionName = defaultConnectionName;
958
- }
959
- /**
960
- * Get a connection by name.
961
- *
962
- * @param connectionName The name of the connection to look up.
963
- * @return A `Connection`
964
- * @throws An `Error` if no connection with the given name exists.
965
- */
966
- async getConnection(connectionName) {
967
- if (connectionName === undefined) {
968
- if (this.defaultConnectionName !== undefined) {
969
- connectionName = this.defaultConnectionName;
970
- }
971
- else {
972
- throw new Error('No default connection.');
973
- }
974
- }
975
- const connection = this.connections.get(connectionName);
976
- if (connection !== undefined) {
977
- return Promise.resolve(connection);
978
- }
979
- else {
980
- throw new Error(`No connection found with name ${connectionName}.`);
981
- }
982
- }
983
- /**
984
- * Gets a list of registered connections.
985
- *
986
- * @return The list of registered connections.
987
- */
988
- listConnections() {
989
- return Array.from(this.connections.values());
990
- }
991
- async lookupConnection(connectionName) {
992
- return this.getConnection(connectionName);
993
- }
994
- static fromArray(connections) {
995
- return new FixedConnectionMap(new Map(connections.map(connection => [connection.name, connection])));
996
- }
997
- }
998
- exports.FixedConnectionMap = FixedConnectionMap;
999
- /**
1000
- * The relationship of an `Explore` to its source.
1001
- */
1002
- var SourceRelationship;
1003
- (function (SourceRelationship) {
1004
- /**
1005
- * The `Explore` is nested data within the source's rows.
1006
- */
1007
- SourceRelationship["Nested"] = "nested";
1008
- /**
1009
- * The `Explore` is the base table.
1010
- */
1011
- SourceRelationship["BaseTable"] = "base_table";
1012
- /**
1013
- * The `Explore` is joined to its source
1014
- */
1015
- SourceRelationship["Cross"] = "cross";
1016
- SourceRelationship["One"] = "one";
1017
- SourceRelationship["Many"] = "many";
1018
- // TODO document this
1019
- SourceRelationship["Inline"] = "inline";
1020
- })(SourceRelationship || (exports.SourceRelationship = SourceRelationship = {}));
1021
- class Entity {
1022
- constructor(name, parent, source) {
1023
- this._name = name;
1024
- this._parent = parent;
1025
- this._source = source;
1026
- }
1027
- get source() {
1028
- return this.source;
1029
- }
1030
- get name() {
1031
- return this._name;
1032
- }
1033
- get sourceClasses() {
1034
- const sourceClasses = [];
1035
- if (this.source) {
1036
- sourceClasses.push(this.source.name);
1037
- }
1038
- sourceClasses.push(this.name);
1039
- return sourceClasses;
1040
- }
1041
- get fieldPath() {
1042
- const path = [this.name];
1043
- let f = this._parent;
1044
- while (f) {
1045
- path.unshift(f.name);
1046
- f = f._parent;
1047
- }
1048
- return path;
1049
- }
1050
- hasParentExplore() {
1051
- return this._parent !== undefined;
1052
- }
1053
- isExplore() {
1054
- return this instanceof Explore;
1055
- }
1056
- isQuery() {
1057
- return this instanceof QueryField;
1058
- }
1059
- }
1060
- class Explore extends Entity {
1061
- constructor(structDef, parentExplore, source) {
1062
- super(structDef.as || structDef.name, parentExplore, source);
1063
- this._structDef = structDef;
1064
- this._parentExplore = parentExplore;
1065
- this.sourceExplore = source;
1066
- }
1067
- get source() {
1068
- return this.sourceExplore;
1069
- }
1070
- isIntrinsic() {
1071
- // Don't have the right discriminator, if this was a field def at some point, the field-defness ahs been lost.
1072
- // A record or an array might be atomic, AND be a structdef, unless they are a dimension (i.e. have a an
1073
- // expression)
1074
- if ((0, model_1.isAtomicFieldType)(this._structDef.type)) {
1075
- return !('e' in this._structDef);
1076
- }
1077
- return false;
1078
- }
1079
- isExploreField() {
1080
- return false;
1081
- }
1082
- tagParse(spec) {
1083
- return (0, annotation_1.annotationToTag)(this._structDef.annotation, spec);
1084
- }
1085
- getTaglines(prefix) {
1086
- return (0, annotation_1.annotationToTaglines)(this._structDef.annotation, prefix);
1087
- }
1088
- get modelTag() {
1089
- this.parsedModelTag || (this.parsedModelTag = (0, annotation_1.annotationToTag)(this._structDef.modelAnnotation).tag);
1090
- return this.parsedModelTag;
1091
- }
1092
- /**
1093
- * @return The name of the entity.
1094
- */
1095
- get name() {
1096
- return this.structDef.as || this.structDef.name;
1097
- }
1098
- getQueryByName(name) {
1099
- const structRef = this.sourceStructDef;
1100
- if (!structRef) {
1101
- throw new Error(`Cannot get query by name from a struct of type ${this.structDef.type}`);
1102
- }
1103
- const view = structRef.fields.find(f => { var _a; return ((_a = f.as) !== null && _a !== void 0 ? _a : f.name) === name; });
1104
- if (view === undefined) {
1105
- throw new Error(`No such view named \`${name}\``);
1106
- }
1107
- if (view.type !== 'turtle') {
1108
- throw new Error(`\`${name}\` is not a view`);
1109
- }
1110
- const internalQuery = {
1111
- type: 'query',
1112
- structRef,
1113
- pipeline: view.pipeline,
1114
- };
1115
- return new PreparedQuery(internalQuery, this.modelDef, [], name);
1116
- }
1117
- get modelDef() {
1118
- if (!(0, model_1.isSourceDef)(this.structDef)) {
1119
- throw new Error(`Cannot create pseudo model for struct type ${this.structDef.type}`);
1120
- }
1121
- return {
1122
- name: 'generated_model',
1123
- exports: [],
1124
- contents: { [this.structDef.name]: this.structDef },
1125
- queryList: [],
1126
- dependencies: {},
1127
- };
1128
- }
1129
- getSingleExploreModel() {
1130
- return new Model(this.modelDef, [], []);
1131
- }
1132
- get fieldMap() {
1133
- var _a;
1134
- if (this._fieldMap === undefined) {
1135
- const sourceFields = ((_a = this.source) === null || _a === void 0 ? void 0 : _a.fieldMap) || new Map();
1136
- this._fieldMap = new Map(this.structDef.fields.map(fieldDef => {
1137
- const name = fieldDef.as || fieldDef.name;
1138
- const sourceField = sourceFields.get(fieldDef.name);
1139
- if ((0, model_1.isJoined)(fieldDef)) {
1140
- return [name, new ExploreField(fieldDef, this, sourceField)];
1141
- }
1142
- else if (fieldDef.type === 'turtle') {
1143
- return [name, new QueryField(fieldDef, this, sourceField)];
1144
- }
1145
- else {
1146
- if (fieldDef.type === 'string') {
1147
- return [name, new StringField(fieldDef, this, sourceField)];
1148
- }
1149
- else if (fieldDef.type === 'number') {
1150
- return [name, new NumberField(fieldDef, this, sourceField)];
1151
- }
1152
- else if (fieldDef.type === 'date') {
1153
- // TODO this is a hack
1154
- // Is this a bug? The extraction functions don't seem like they should return a
1155
- // field of type "date". Rather, they should be of type "number".
1156
- if (fieldDef.timeframe &&
1157
- ['day_of_month', 'day_of_week', 'day_of_year'].includes(fieldDef.timeframe)) {
1158
- return [
1159
- name,
1160
- new NumberField({ ...fieldDef, type: 'number' }, this, sourceField),
1161
- ];
1162
- }
1163
- return [name, new DateField(fieldDef, this, sourceField)];
1164
- }
1165
- else if (fieldDef.type === 'timestamp') {
1166
- return [name, new TimestampField(fieldDef, this, sourceField)];
1167
- }
1168
- else if (fieldDef.type === 'timestamptz') {
1169
- return [name, new TimestampField(fieldDef, this, sourceField)];
1170
- }
1171
- else if (fieldDef.type === 'boolean') {
1172
- return [name, new BooleanField(fieldDef, this, sourceField)];
1173
- }
1174
- else if (fieldDef.type === 'json') {
1175
- return [name, new JSONField(fieldDef, this, sourceField)];
1176
- }
1177
- else if (fieldDef.type === 'sql native') {
1178
- return [name, new UnsupportedField(fieldDef, this, sourceField)];
1179
- }
1180
- }
1181
- }));
1182
- }
1183
- return this._fieldMap;
1184
- }
1185
- get allFields() {
1186
- return [...this.fieldMap.values()];
1187
- }
1188
- get allFieldsWithOrder() {
1189
- var _a, _b, _c;
1190
- if (!this._allFieldsWithOrder) {
1191
- const orderByFields = [
1192
- ...(((_c = (_b = (_a = this.sourceStructDef) === null || _a === void 0 ? void 0 : _a.resultMetadata) === null || _b === void 0 ? void 0 : _b.orderBy) === null || _c === void 0 ? void 0 : _c.map(f => {
1193
- if (typeof f.field === 'string') {
1194
- const a = {
1195
- field: this.fieldMap.get(f.field),
1196
- dir: f.dir,
1197
- };
1198
- return a;
1199
- }
1200
- throw new Error('Does not support mapping order by from number.');
1201
- })) || []),
1202
- ];
1203
- const orderByFieldSet = new Set(orderByFields.map(f => f.field.name));
1204
- this._allFieldsWithOrder = [
1205
- ...orderByFields,
1206
- ...this.allFields
1207
- .filter(f => !orderByFieldSet.has(f.name))
1208
- .map(field => {
1209
- return {
1210
- field,
1211
- dir: 'asc',
1212
- };
1213
- }),
1214
- ];
1215
- }
1216
- return this._allFieldsWithOrder;
1217
- }
1218
- get intrinsicFields() {
1219
- return [...this.fieldMap.values()].filter(f => f.isIntrinsic());
1220
- }
1221
- get dimensions() {
1222
- return [...this.allFieldsWithOrder].filter(f => f.field.isAtomicField() && f.field.sourceWasDimension());
1223
- }
1224
- getFieldByName(fieldName) {
1225
- const field = this.fieldMap.get(fieldName);
1226
- if (field === undefined) {
1227
- throw new Error(`No such field ${fieldName}.`);
1228
- }
1229
- return field;
1230
- }
1231
- getFieldByNameIfExists(fieldName) {
1232
- return this.fieldMap.get(fieldName);
1233
- }
1234
- get primaryKey() {
1235
- var _a;
1236
- return (_a = this.sourceStructDef) === null || _a === void 0 ? void 0 : _a.primaryKey;
1237
- }
1238
- get parentExplore() {
1239
- return this._parentExplore;
1240
- }
1241
- hasParentExplore() {
1242
- return this instanceof ExploreField;
1243
- }
1244
- // TODO wrapper type for FilterCondition
1245
- get filters() {
1246
- var _a;
1247
- if ((0, model_1.isSourceDef)(this.structDef)) {
1248
- return ((_a = this.structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.filterList) || [];
1249
- }
1250
- return [];
1251
- }
1252
- get limit() {
1253
- var _a, _b;
1254
- return (_b = (_a = this.sourceStructDef) === null || _a === void 0 ? void 0 : _a.resultMetadata) === null || _b === void 0 ? void 0 : _b.limit;
1255
- }
1256
- get structDef() {
1257
- return this._structDef;
1258
- }
1259
- get queryTimezone() {
1260
- var _a;
1261
- return (_a = this.sourceStructDef) === null || _a === void 0 ? void 0 : _a.queryTimezone;
1262
- }
1263
- get sourceStructDef() {
1264
- if ((0, model_1.isSourceDef)(this.structDef)) {
1265
- return this.structDef;
1266
- }
1267
- }
1268
- toJSON() {
1269
- var _a, _b;
1270
- return {
1271
- _structDef: this._structDef,
1272
- sourceExplore: (_a = this.sourceExplore) === null || _a === void 0 ? void 0 : _a.toJSON(),
1273
- _parentExplore: (_b = this._parentExplore) === null || _b === void 0 ? void 0 : _b.toJSON(),
1274
- };
1275
- }
1276
- static fromJSON(main_explore) {
1277
- const parentExplore = main_explore._parentExplore !== undefined
1278
- ? Explore.fromJSON(main_explore._parentExplore)
1279
- : undefined;
1280
- const sourceExplore = main_explore.sourceExplore !== undefined
1281
- ? Explore.fromJSON(main_explore.sourceExplore)
1282
- : undefined;
1283
- return new Explore(main_explore._structDef, parentExplore, sourceExplore);
1284
- }
1285
- get location() {
1286
- return this.structDef.location;
1287
- }
1288
- collectSourceComponents(structDef) {
1289
- const sources = [];
1290
- if (structDef.type === 'composite') {
1291
- for (const source of structDef.sources) {
1292
- sources.push(...this.collectSourceComponents(source));
1293
- }
1294
- return sources;
1295
- }
1296
- if (isSourceComponent(structDef)) {
1297
- if (structDef.type === 'table') {
1298
- // Generate componentID based on connection and table name
1299
- sources.push({
1300
- type: 'table',
1301
- tableName: structDef.tablePath,
1302
- componentID: `${structDef.connection}:${structDef.tablePath}`,
1303
- sourceID: `${structDef.connection}:${structDef.tablePath}`,
1304
- });
1305
- }
1306
- else if (structDef.type === 'sql_select') {
1307
- sources.push({
1308
- type: 'sql',
1309
- selectStatement: structDef.selectStr,
1310
- componentID: `${structDef.connection}:${structDef.selectStr}`,
1311
- sourceID: `${structDef.connection}:${structDef.selectStr}`,
1312
- });
1313
- }
1314
- else if (structDef.type === 'query_source') {
1315
- // For QuerySourceDef, we need to extract the SQL from the query
1316
- // We need to create a PreparedQuery from the query, then get a PreparedResult
1317
- // to access the SQL
1318
- let sql;
1319
- try {
1320
- // Create a PreparedQuery from the query in the QuerySourceDef
1321
- const preparedQuery = new PreparedQuery(structDef.query, this.modelDef, []);
1322
- // Get the PreparedResult which contains the SQL
1323
- const preparedResult = preparedQuery.getPreparedResult();
1324
- // Extract the SQL
1325
- sql = preparedResult.sql;
1326
- }
1327
- catch (error) {
1328
- // If we can't compile the query, use a placeholder
1329
- sql = `-- Could not compile SQL for query ${structDef.query.name || 'unnamed query'}: ${error instanceof Error ? error.message : String(error)}`;
1330
- }
1331
- // Generate componentID based on connection and SQL
1332
- const componentID = `${structDef.connection}:${sql}`;
1333
- sources.push({
1334
- type: 'sql',
1335
- selectStatement: sql,
1336
- componentID: componentID,
1337
- sourceID: componentID,
1338
- });
1339
- }
1340
- }
1341
- else {
1342
- return [];
1343
- }
1344
- // Process all fields to find joins
1345
- for (const field of structDef.fields) {
1346
- if ((0, model_1.isJoined)(field)) {
1347
- sources.push(...this.collectSourceComponents(field));
1348
- }
1349
- }
1350
- return sources;
1351
- }
1352
- /**
1353
- * THIS IS A HIGHLY EXPERIMENTAL API AND MAY VANISH OR CHANGE WITHOUT NOTICE
1354
- */
1355
- getSourceComponents() {
1356
- const uniqueSources = {};
1357
- if ((0, model_1.isSourceDef)(this.structDef)) {
1358
- const allSources = this.collectSourceComponents(this.structDef);
1359
- // Deduplicate sources using componentID as the key
1360
- for (const source of allSources) {
1361
- if (source.componentID) {
1362
- uniqueSources[source.componentID] = source;
1363
- }
1364
- else if (source.sourceID) {
1365
- uniqueSources[source.sourceID] = source;
1366
- }
1367
- }
1368
- }
1369
- // Return the deduplicated sources as an array
1370
- return Object.values(uniqueSources);
1371
- }
1372
- }
1373
- exports.Explore = Explore;
1374
- var AtomicFieldType;
1375
- (function (AtomicFieldType) {
1376
- AtomicFieldType["String"] = "string";
1377
- AtomicFieldType["Number"] = "number";
1378
- AtomicFieldType["Boolean"] = "boolean";
1379
- AtomicFieldType["Date"] = "date";
1380
- AtomicFieldType["Timestamp"] = "timestamp";
1381
- AtomicFieldType["Timestamptz"] = "timestamptz";
1382
- AtomicFieldType["Json"] = "json";
1383
- AtomicFieldType["NativeUnsupported"] = "sql native";
1384
- AtomicFieldType["Error"] = "error";
1385
- })(AtomicFieldType || (exports.AtomicFieldType = AtomicFieldType = {}));
1386
- class AtomicField extends Entity {
1387
- constructor(fieldTypeDef, parent, source) {
1388
- super(fieldTypeDef.as || fieldTypeDef.name, parent, source);
1389
- this.fieldTypeDef = fieldTypeDef;
1390
- this.parent = parent;
1391
- }
1392
- get type() {
1393
- switch (this.fieldTypeDef.type) {
1394
- case 'string':
1395
- return AtomicFieldType.String;
1396
- case 'boolean':
1397
- return AtomicFieldType.Boolean;
1398
- case 'date':
1399
- return AtomicFieldType.Date;
1400
- case 'timestamp':
1401
- return AtomicFieldType.Timestamp;
1402
- case 'timestamptz':
1403
- return AtomicFieldType.Timestamptz;
1404
- case 'number':
1405
- return AtomicFieldType.Number;
1406
- case 'json':
1407
- return AtomicFieldType.Json;
1408
- case 'sql native':
1409
- return AtomicFieldType.NativeUnsupported;
1410
- case 'error':
1411
- return AtomicFieldType.Error;
1412
- case 'record':
1413
- case 'array':
1414
- throw new Error(`MTOY TODO IMPLEMENT Atomic ${this.fieldTypeDef.type}`);
1415
- default: {
1416
- const x = this.fieldTypeDef;
1417
- throw new Error(`Can't make an atomic field from ${x}`);
1418
- }
1419
- }
1420
- }
1421
- tagParse(spec) {
1422
- spec = (0, annotation_1.addModelScope)(spec, this.parent.modelTag);
1423
- return (0, annotation_1.annotationToTag)(this.fieldTypeDef.annotation, spec);
1424
- }
1425
- getTaglines(prefix) {
1426
- return (0, annotation_1.annotationToTaglines)(this.fieldTypeDef.annotation, prefix);
1427
- }
1428
- isIntrinsic() {
1429
- return (0, model_1.fieldIsIntrinsic)(this.fieldTypeDef);
1430
- }
1431
- isQueryField() {
1432
- return false;
1433
- }
1434
- isExploreField() {
1435
- return false;
1436
- }
1437
- isAtomicField() {
1438
- return true;
1439
- }
1440
- isCalculation() {
1441
- //return !!this.fieldTypeDef.aggregate;
1442
- return (0, model_1.expressionIsCalculation)(this.fieldTypeDef.expressionType);
1443
- }
1444
- get sourceField() {
1445
- // TODO
1446
- throw new Error();
1447
- }
1448
- get sourceClasses() {
1449
- const sourceField = this.fieldTypeDef.name || this.fieldTypeDef.as;
1450
- return sourceField ? [sourceField] : [];
1451
- }
1452
- /**
1453
- * A unique ID of this field within the context of a result; undefined
1454
- * for fields that are not derived from a Result.
1455
- */
1456
- get referenceId() {
1457
- var _a;
1458
- return (_a = this.fieldTypeDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.referenceId;
1459
- }
1460
- // was the field generated from a measure in the previous query
1461
- sourceWasMeasure() {
1462
- var _a;
1463
- return ((_a = this.fieldTypeDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.fieldKind) === 'measure';
1464
- }
1465
- sourceWasMeasureLike() {
1466
- var _a, _b;
1467
- return (((_a = this.fieldTypeDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.fieldKind) === 'measure' ||
1468
- ((_b = this.fieldTypeDef.resultMetadata) === null || _b === void 0 ? void 0 : _b.fieldKind) === 'struct');
1469
- }
1470
- sourceWasDimension() {
1471
- var _a;
1472
- return ((_a = this.fieldTypeDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.fieldKind) === 'dimension';
1473
- }
1474
- hasParentExplore() {
1475
- return true;
1476
- }
1477
- isString() {
1478
- return this instanceof StringField;
1479
- }
1480
- isNumber() {
1481
- return this instanceof NumberField;
1482
- }
1483
- isDate() {
1484
- return this instanceof DateField;
1485
- }
1486
- isBoolean() {
1487
- return this instanceof BooleanField;
1488
- }
1489
- isJSON() {
1490
- return this instanceof JSONField;
1491
- }
1492
- isTimestamp() {
1493
- return this instanceof TimestampField;
1494
- }
1495
- isUnsupported() {
1496
- return this instanceof UnsupportedField;
1497
- }
1498
- get parentExplore() {
1499
- return this.parent;
1500
- }
1501
- /**
1502
- * @return Field name for drill.
1503
- */
1504
- get expression() {
1505
- const dot = '.';
1506
- const resultMetadata = this.fieldTypeDef.resultMetadata;
1507
- // If field is joined-in from another table i.e. of type `tableName.columnName`,
1508
- // return sourceField, else return name because this could be a renamed field.
1509
- return ((resultMetadata === null || resultMetadata === void 0 ? void 0 : resultMetadata.sourceExpression) ||
1510
- ((resultMetadata === null || resultMetadata === void 0 ? void 0 : resultMetadata.sourceField.includes(dot))
1511
- ? resultMetadata === null || resultMetadata === void 0 ? void 0 : resultMetadata.sourceField
1512
- : this.name));
1513
- }
1514
- get location() {
1515
- return this.fieldTypeDef.location;
1516
- }
1517
- }
1518
- exports.AtomicField = AtomicField;
1519
- var DateTimeframe;
1520
- (function (DateTimeframe) {
1521
- DateTimeframe["Day"] = "day";
1522
- DateTimeframe["Week"] = "week";
1523
- DateTimeframe["Month"] = "month";
1524
- DateTimeframe["Quarter"] = "quarter";
1525
- DateTimeframe["Year"] = "year";
1526
- })(DateTimeframe || (exports.DateTimeframe = DateTimeframe = {}));
1527
- var TimestampTimeframe;
1528
- (function (TimestampTimeframe) {
1529
- TimestampTimeframe["Day"] = "day";
1530
- TimestampTimeframe["Week"] = "week";
1531
- TimestampTimeframe["Month"] = "month";
1532
- TimestampTimeframe["Quarter"] = "quarter";
1533
- TimestampTimeframe["Year"] = "year";
1534
- TimestampTimeframe["Second"] = "second";
1535
- TimestampTimeframe["Hour"] = "hour";
1536
- TimestampTimeframe["Minute"] = "minute";
1537
- })(TimestampTimeframe || (exports.TimestampTimeframe = TimestampTimeframe = {}));
1538
- class DateField extends AtomicField {
1539
- constructor(fieldDateDef, parent, source) {
1540
- super(fieldDateDef, parent, source);
1541
- this.fieldDateDef = fieldDateDef;
1542
- }
1543
- get timeframe() {
1544
- if (this.fieldDateDef.timeframe === undefined) {
1545
- return undefined;
1546
- }
1547
- switch (this.fieldDateDef.timeframe) {
1548
- case 'day':
1549
- return DateTimeframe.Day;
1550
- case 'week':
1551
- return DateTimeframe.Week;
1552
- case 'month':
1553
- return DateTimeframe.Month;
1554
- case 'quarter':
1555
- return DateTimeframe.Quarter;
1556
- case 'year':
1557
- return DateTimeframe.Year;
1558
- }
1559
- }
1560
- }
1561
- exports.DateField = DateField;
1562
- class TimestampField extends AtomicField {
1563
- constructor(fieldTimestampDef, parent, source) {
1564
- super(fieldTimestampDef, parent, source);
1565
- this.fieldTimestampDef = fieldTimestampDef;
1566
- }
1567
- get timeframe() {
1568
- if (this.fieldTimestampDef.timeframe === undefined) {
1569
- return undefined;
1570
- }
1571
- switch (this.fieldTimestampDef.timeframe) {
1572
- case 'day':
1573
- return TimestampTimeframe.Day;
1574
- case 'week':
1575
- return TimestampTimeframe.Week;
1576
- case 'month':
1577
- return TimestampTimeframe.Month;
1578
- case 'quarter':
1579
- return TimestampTimeframe.Quarter;
1580
- case 'year':
1581
- return TimestampTimeframe.Year;
1582
- case 'second':
1583
- return TimestampTimeframe.Second;
1584
- case 'hour':
1585
- return TimestampTimeframe.Hour;
1586
- case 'minute':
1587
- return TimestampTimeframe.Minute;
1588
- }
1589
- }
1590
- }
1591
- exports.TimestampField = TimestampField;
1592
- class NumberField extends AtomicField {
1593
- constructor(fieldNumberDef, parent, source) {
1594
- super(fieldNumberDef, parent, source);
1595
- this.fieldNumberDef = fieldNumberDef;
1596
- }
1597
- }
1598
- exports.NumberField = NumberField;
1599
- class BooleanField extends AtomicField {
1600
- constructor(fieldBooleanDef, parent, source) {
1601
- super(fieldBooleanDef, parent, source);
1602
- this.fieldBooleanDef = fieldBooleanDef;
1603
- }
1604
- }
1605
- exports.BooleanField = BooleanField;
1606
- class JSONField extends AtomicField {
1607
- constructor(fieldJSONDef, parent, source) {
1608
- super(fieldJSONDef, parent, source);
1609
- this.fieldJSONDef = fieldJSONDef;
1610
- }
1611
- }
1612
- exports.JSONField = JSONField;
1613
- class UnsupportedField extends AtomicField {
1614
- constructor(fieldUnsupportedDef, parent, source) {
1615
- super(fieldUnsupportedDef, parent, source);
1616
- this.fieldUnsupportedDef = fieldUnsupportedDef;
1617
- }
1618
- get rawType() {
1619
- return this.fieldUnsupportedDef.rawType;
1620
- }
1621
- }
1622
- exports.UnsupportedField = UnsupportedField;
1623
- class StringField extends AtomicField {
1624
- constructor(fieldStringDef, parent, source) {
1625
- super(fieldStringDef, parent, source);
1626
- this.fieldStringDef = fieldStringDef;
1627
- }
1628
- }
1629
- exports.StringField = StringField;
1630
- class Query extends Entity {
1631
- constructor(turtleDef, parent, source) {
1632
- super(turtleDef.as || turtleDef.name, parent, source);
1633
- this.turtleDef = turtleDef;
1634
- }
1635
- get source() {
1636
- return this.sourceQuery;
1637
- }
1638
- isIntrinsic() {
1639
- return false;
1640
- }
1641
- get location() {
1642
- return this.turtleDef.location;
1643
- }
1644
- }
1645
- exports.Query = Query;
1646
- class QueryField extends Query {
1647
- constructor(turtleDef, parent, source) {
1648
- super(turtleDef, parent, source);
1649
- this.parent = parent;
1650
- }
1651
- tagParse(spec) {
1652
- spec = (0, annotation_1.addModelScope)(spec, this.parent.modelTag);
1653
- return (0, annotation_1.annotationToTag)(this.turtleDef.annotation, spec);
1654
- }
1655
- getTaglines(prefix) {
1656
- return (0, annotation_1.annotationToTaglines)(this.turtleDef.annotation, prefix);
1657
- }
1658
- isQueryField() {
1659
- return true;
1660
- }
1661
- isExploreField() {
1662
- return false;
1663
- }
1664
- isAtomicField() {
1665
- return false;
1666
- }
1667
- get sourceClasses() {
1668
- const sourceField = this.turtleDef.name || this.turtleDef.as;
1669
- return sourceField ? [sourceField] : [];
1670
- }
1671
- hasParentExplore() {
1672
- return true;
1673
- }
1674
- get parentExplore() {
1675
- return this.parent;
1676
- }
1677
- get expression() {
1678
- return this.name;
1679
- }
1680
- }
1681
- exports.QueryField = QueryField;
1682
- var JoinRelationship;
1683
- (function (JoinRelationship) {
1684
- JoinRelationship["OneToOne"] = "one_to_one";
1685
- JoinRelationship["OneToMany"] = "one_to_many";
1686
- JoinRelationship["ManyToOne"] = "many_to_one";
1687
- })(JoinRelationship || (exports.JoinRelationship = JoinRelationship = {}));
1688
- class ExploreField extends Explore {
1689
- constructor(structDef, parentExplore, source) {
1690
- super(structDef, parentExplore, source);
1691
- this._parentExplore = parentExplore;
1692
- }
1693
- get joinRelationship() {
1694
- if ((0, model_1.isJoined)(this.structDef)) {
1695
- switch (this.structDef.join) {
1696
- case 'one':
1697
- return JoinRelationship.OneToOne;
1698
- case 'many':
1699
- case 'cross':
1700
- return JoinRelationship.ManyToOne;
1701
- }
1702
- }
1703
- throw new Error('A source field must have a join relationship.');
1704
- }
1705
- get isRecord() {
1706
- return this.joinRelationship === JoinRelationship.OneToOne;
1707
- }
1708
- get isArray() {
1709
- return this.joinRelationship !== JoinRelationship.OneToOne;
1710
- }
1711
- tagParse(spec) {
1712
- spec = (0, annotation_1.addModelScope)(spec, this._parentExplore.modelTag);
1713
- return (0, annotation_1.annotationToTag)(this._structDef.annotation, spec);
1714
- }
1715
- isQueryField() {
1716
- return false;
1717
- }
1718
- isExploreField() {
1719
- return true;
1720
- }
1721
- isAtomicField() {
1722
- return false;
1723
- }
1724
- get parentExplore() {
1725
- return this._parentExplore;
1726
- }
1727
- get sourceClasses() {
1728
- const sourceField = this.structDef.name || this.structDef.as;
1729
- return sourceField ? [sourceField] : [];
1730
- }
1731
- get queryTimezone() {
1732
- // For ExploreField, check the structDef directly first
1733
- if ((0, model_1.isRecordOrRepeatedRecord)(this._structDef)) {
1734
- return this._structDef.queryTimezone;
1735
- }
1736
- // Fall back to the parent implementation
1737
- return super.queryTimezone;
1738
- }
1739
- }
1740
- exports.ExploreField = ExploreField;
1741
- /**
1742
- * An environment for compiling and running Malloy queries.
1743
- */
1744
- class Runtime {
1745
- constructor({ urlReader, connections, connection, eventStream, cacheManager, }) {
1746
- this.isTestRuntime = false;
1747
- if (connections === undefined) {
1748
- if (connection === undefined) {
1749
- throw new Error('A LookupConnection<Connection> or Connection is required.');
1750
- }
1751
- connections = {
1752
- lookupConnection: () => Promise.resolve(connection),
1753
- };
1754
- }
1755
- if (urlReader === undefined) {
1756
- urlReader = new EmptyURLReader();
1757
- }
1758
- this._urlReader = urlReader;
1759
- this._connections = connections;
1760
- this._eventStream = eventStream;
1761
- this._cacheManager = cacheManager;
1762
- }
1763
- /**
1764
- * @return The `CacheManager` for this runtime instance.
1765
- */
1766
- get cacheManager() {
1767
- return this._cacheManager;
1768
- }
1769
- /**
1770
- * @return The `URLReader` for this runtime instance.
1771
- */
1772
- get urlReader() {
1773
- return this._urlReader;
1774
- }
1775
- /**
1776
- * @return The `LookupConnection<Connection>` for this runtime instance.
1777
- */
1778
- get connections() {
1779
- return this._connections;
1780
- }
1781
- /**
1782
- * @return The `EventStream` for this runtime instance.
1783
- */
1784
- get eventStream() {
1785
- return this._eventStream;
1786
- }
1787
- /**
1788
- * Load a Malloy model by URL or contents.
1789
- *
1790
- * @param source The model URL or contents to load and (eventually) compile.
1791
- * @return A `ModelMaterializer` capable of materializing the requested model,
1792
- * or loading further related objects.
1793
- */
1794
- loadModel(source, options) {
1795
- const { refreshSchemaCache, noThrowOnError } = options || {};
1796
- if (this.isTestRuntime) {
1797
- if (options === undefined) {
1798
- options = { testEnvironment: true };
1799
- }
1800
- else {
1801
- options = { ...options, testEnvironment: true };
1802
- }
1803
- }
1804
- const compilable = source instanceof URL ? { url: source } : { source };
1805
- return new ModelMaterializer(this, async () => {
1806
- return Malloy.compile({
1807
- ...compilable,
1808
- urlReader: this.urlReader,
1809
- connections: this.connections,
1810
- refreshSchemaCache,
1811
- noThrowOnError,
1812
- eventStream: this.eventStream,
1813
- replaceMaterializedReferences: options === null || options === void 0 ? void 0 : options.replaceMaterializedReferences,
1814
- materializedTablePrefix: options === null || options === void 0 ? void 0 : options.materializedTablePrefix,
1815
- importBaseURL: options === null || options === void 0 ? void 0 : options.importBaseURL,
1816
- testEnvironment: options === null || options === void 0 ? void 0 : options.testEnvironment,
1817
- cacheManager: this.cacheManager,
1818
- });
1819
- }, options);
1820
- }
1821
- // TODO Consider formalizing this. Perhaps as a `withModel` method,
1822
- // as well as a `Model.fromModelDefinition` if we choose to expose
1823
- // `ModelDef` to the world formally. For now, this should only
1824
- // be used in tests.
1825
- _loadModelFromModelDef(modelDef, options) {
1826
- return new ModelMaterializer(this, async () => {
1827
- return new Model(modelDef, [], []);
1828
- }, options);
1829
- }
1830
- /**
1831
- * Load a Malloy query by URL or contents.
1832
- *
1833
- * @param query The query URL or contents to load and (eventually) compile.
1834
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
1835
- * or loading further related objects.
1836
- */
1837
- loadQuery(query, options) {
1838
- return this.loadModel(query, options).loadFinalQuery();
1839
- }
1840
- /**
1841
- * Load a Malloy query by the URL or contents of a Malloy model document
1842
- * and the index of an unnamed query contained in the model.
1843
- *
1844
- * @param model The model URL or contents to load and (eventually) compile to retrieve the requested query.
1845
- * @param index The index of the query to use within the model.
1846
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
1847
- * or loading further related objects.
1848
- */
1849
- loadQueryByIndex(model, index, options) {
1850
- return this.loadModel(model, options).loadQueryByIndex(index, options);
1851
- }
1852
- /**
1853
- * Load a Malloy query by the URL or contents of a Malloy model document
1854
- * and the name of a query contained in the model.
1855
- *
1856
- * @param model The model URL or contents to load and (eventually) compile to retrieve the requested query.
1857
- * @param name The name of the query to use within the model.
1858
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
1859
- * or loading further related objects.
1860
- */
1861
- loadQueryByName(model, name, options) {
1862
- return this.loadModel(model, options).loadQueryByName(name, options);
1863
- }
1864
- // TODO maybe use overloads for the alternative parameters
1865
- /**
1866
- * Compile a Malloy model by URL or contents.
1867
- *
1868
- * @param source The URL or contents of a Malloy model document to compile.
1869
- * @return A promise of a compiled `Model`.
1870
- */
1871
- getModel(source, options) {
1872
- return this.loadModel(source, options).getModel();
1873
- }
1874
- /**
1875
- * Compile a Malloy query by URL or contents.
1876
- *
1877
- * @param query The URL or contents of a Malloy query document to compile.
1878
- * @return A promise of a compiled `PreparedQuery`.
1879
- */
1880
- getQuery(query, options) {
1881
- return this.loadQuery(query, options).getPreparedQuery();
1882
- }
1883
- /**
1884
- * Compile a Malloy query by the URL or contents of a model document
1885
- * and the index of an unnamed query contained within the model.
1886
- *
1887
- * @param model The URL or contents of a Malloy model document to compile.
1888
- * @param index The index of an unnamed query contained within the model.
1889
- * @return A promise of a compiled `PreparedQuery`.
1890
- */
1891
- getQueryByIndex(model, index, options) {
1892
- return this.loadQueryByIndex(model, index, options).getPreparedQuery();
1893
- }
1894
- /**
1895
- * Compile a Malloy query by the URL or contents of a model document
1896
- * and the name of a query contained within the model.
1897
- *
1898
- * @param model The URL or contents of a Malloy model document to compile.
1899
- * @param name The name of a query contained within the model.
1900
- * @return A promise of a compiled `PreparedQuery`.
1901
- */
1902
- getQueryByName(model, name, options) {
1903
- return this.loadQueryByName(model, name, options).getPreparedQuery();
1904
- }
1905
- }
1906
- exports.Runtime = Runtime;
1907
- class ConnectionRuntime extends Runtime {
1908
- constructor({ urlReader, connections, }) {
1909
- super({
1910
- connections: FixedConnectionMap.fromArray(connections),
1911
- urlReader,
1912
- });
1913
- this.rawConnections = connections;
1914
- }
1915
- }
1916
- exports.ConnectionRuntime = ConnectionRuntime;
1917
- class SingleConnectionRuntime extends Runtime {
1918
- constructor({ urlReader, connection, eventStream, cacheManager, }) {
1919
- super({
1920
- urlReader,
1921
- eventStream,
1922
- cacheManager,
1923
- connection,
1924
- });
1925
- this.connection = connection;
1926
- }
1927
- get supportsNesting() {
1928
- return (0, dialect_1.getDialect)(this.connection.dialectName).supportsNesting;
1929
- }
1930
- // quote a column name
1931
- quote(column) {
1932
- return (0, dialect_1.getDialect)(this.connection.dialectName).sqlMaybeQuoteIdentifier(column);
1933
- }
1934
- get dialect() {
1935
- return (0, dialect_1.getDialect)(this.connection.dialectName);
1936
- }
1937
- getQuoter() {
1938
- return (x) => this.quote(x.toString());
1939
- }
1940
- }
1941
- exports.SingleConnectionRuntime = SingleConnectionRuntime;
1942
- class FluentState {
1943
- constructor(runtime, materialize) {
1944
- this.runtime = runtime;
1945
- this._materialize = materialize;
1946
- }
1947
- materialize() {
1948
- if (this.materialized === undefined) {
1949
- return this.rematerialize();
1950
- }
1951
- return this.materialized;
1952
- }
1953
- rematerialize() {
1954
- this.materialized = this._materialize();
1955
- return this.materialized;
1956
- }
1957
- makeQueryMaterializer(materialize, options) {
1958
- return new QueryMaterializer(this.runtime, materialize, options);
1959
- }
1960
- makeExploreMaterializer(materialize, options) {
1961
- return new ExploreMaterializer(this.runtime, materialize, options);
1962
- }
1963
- makePreparedResultMaterializer(materialize) {
1964
- return new PreparedResultMaterializer(this.runtime, materialize);
1965
- }
1966
- }
1967
- /**
1968
- * An object representing the task of loading a `Model`, capable of
1969
- * materializing that model (via `getModel()`) or extending the task to load
1970
- * queries or explores (via e.g. `loadFinalQuery()`, `loadQuery`, `loadExploreByName`, etc.).
1971
- */
1972
- class ModelMaterializer extends FluentState {
1973
- constructor(runtime, materialize, options) {
1974
- super(runtime, materialize);
1975
- this.runtime = runtime;
1976
- this.compileQueryOptions = options;
1977
- }
1978
- /**
1979
- * Load the final (unnamed) Malloy query contained within this loaded `Model`.
1980
- *
1981
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
1982
- * or loading further related objects.
1983
- */
1984
- loadFinalQuery(options) {
1985
- return this.makeQueryMaterializer(async () => {
1986
- return (await this.materialize()).getPreparedQuery();
1987
- }, {
1988
- ...this.compileQueryOptions,
1989
- ...options,
1990
- });
1991
- }
1992
- /**
1993
- * Load an unnamed query contained within this loaded `Model` by index.
1994
- *
1995
- * @param index The index of the query to load.
1996
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
1997
- * or loading further related objects.
1998
- */
1999
- loadQueryByIndex(index, options) {
2000
- return this.makeQueryMaterializer(async () => {
2001
- return (await this.materialize()).getPreparedQueryByIndex(index);
2002
- }, {
2003
- ...this.compileQueryOptions,
2004
- ...options,
2005
- });
2006
- }
2007
- /**
2008
- * Load a query contained within this loaded `Model` by its name.
2009
- *
2010
- * @param name The name of the query to load.
2011
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
2012
- * or loading further related objects.
2013
- */
2014
- loadQueryByName(name, options) {
2015
- return this.makeQueryMaterializer(async () => {
2016
- return (await this.materialize()).getPreparedQueryByName(name);
2017
- }, {
2018
- ...this.compileQueryOptions,
2019
- ...options,
2020
- });
2021
- }
2022
- /**
2023
- * Load a query against this loaded `Model` by its URL or contents.
2024
- *
2025
- * @param query The URL or contents of the query to load and (eventually) compile.
2026
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
2027
- * or loading further related objects.
2028
- */
2029
- loadQuery(query, options) {
2030
- const { refreshSchemaCache, noThrowOnError } = options || {};
2031
- return this.makeQueryMaterializer(async () => {
2032
- const urlReader = this.runtime.urlReader;
2033
- const connections = this.runtime.connections;
2034
- if (this.runtime.isTestRuntime) {
2035
- if (options === undefined) {
2036
- options = { testEnvironment: true };
2037
- }
2038
- else {
2039
- options = { ...options, testEnvironment: true };
2040
- }
2041
- }
2042
- const compilable = query instanceof URL ? { url: query } : { source: query };
2043
- const model = await this.getModel();
2044
- const queryModel = await Malloy.compile({
2045
- ...compilable,
2046
- urlReader,
2047
- connections,
2048
- model,
2049
- refreshSchemaCache,
2050
- noThrowOnError,
2051
- importBaseURL: options === null || options === void 0 ? void 0 : options.importBaseURL,
2052
- testEnvironment: options === null || options === void 0 ? void 0 : options.testEnvironment,
2053
- ...this.compileQueryOptions,
2054
- });
2055
- return queryModel.preparedQuery;
2056
- });
2057
- }
2058
- /**
2059
- * Extend a Malloy model by URL or contents.
2060
- *
2061
- * @param source The model URL or contents to load and (eventually) compile.
2062
- * @return A `ModelMaterializer` capable of materializing the requested model,
2063
- * or loading further related objects.
2064
- */
2065
- extendModel(query, options) {
2066
- if (this.runtime.isTestRuntime) {
2067
- if (options === undefined) {
2068
- options = { testEnvironment: true };
2069
- }
2070
- else {
2071
- options = { ...options, testEnvironment: true };
2072
- }
2073
- }
2074
- return new ModelMaterializer(this.runtime, async () => {
2075
- const urlReader = this.runtime.urlReader;
2076
- const connections = this.runtime.connections;
2077
- const compilable = query instanceof URL ? { url: query } : { source: query };
2078
- const model = await this.getModel();
2079
- const queryModel = await Malloy.compile({
2080
- ...compilable,
2081
- urlReader,
2082
- connections,
2083
- model,
2084
- refreshSchemaCache: options === null || options === void 0 ? void 0 : options.refreshSchemaCache,
2085
- noThrowOnError: options === null || options === void 0 ? void 0 : options.noThrowOnError,
2086
- importBaseURL: options === null || options === void 0 ? void 0 : options.importBaseURL,
2087
- testEnvironment: options === null || options === void 0 ? void 0 : options.testEnvironment,
2088
- ...this.compileQueryOptions,
2089
- });
2090
- return queryModel;
2091
- }, options);
2092
- }
2093
- async search(sourceName, searchTerm, limit = 1000, searchField = undefined, eventStream) {
2094
- const model = await this.materialize();
2095
- const queryModel = new model_1.QueryModel(model._modelDef, eventStream);
2096
- const schema = model.getExploreByName(sourceName).structDef;
2097
- if (!(0, model_1.isSourceDef)(schema)) {
2098
- throw new Error('Source to be searched was unexpectedly, not a source');
2099
- }
2100
- const connectionName = schema.connection;
2101
- const connection = await this.runtime.connections.lookupConnection(connectionName);
2102
- return await queryModel.searchIndex(connection, sourceName, searchTerm, limit, searchField);
2103
- }
2104
- async searchValueMap(sourceName, limit = 10, options) {
2105
- const model = await this.materialize();
2106
- const schema = model.getExploreByName(sourceName);
2107
- if (!(0, model_1.isSourceDef)(schema.structDef)) {
2108
- throw new Error('Source to be searched was unexpectedly, not a source');
2109
- }
2110
- let indexQuery = '{index: *}';
2111
- if (schema.getFieldByNameIfExists('search_index')) {
2112
- indexQuery = 'search_index';
2113
- }
2114
- const searchMapMalloy = `
2115
- run: ${sourceName}
2116
- -> ${indexQuery}
2117
- -> {
2118
- where: fieldType = 'string'
2119
- group_by: fieldName
2120
- aggregate: cardinality is count(fieldValue)
2121
- nest: values is {
2122
- group_by: fieldValue, weight
2123
- order_by: weight desc
2124
- limit: ${limit}
2125
- }
2126
- limit: 1000
2127
- }
2128
- `;
2129
- const result = await this.loadQuery(searchMapMalloy, options).run({
2130
- rowLimit: 1000,
2131
- });
2132
- const rawResult = result._queryResult.result;
2133
- return rawResult.map(row => ({
2134
- ...row,
2135
- cardinality: (0, row_data_utils_1.rowDataToNumber)(row.cardinality),
2136
- values: row.values.map(v => ({
2137
- ...v,
2138
- weight: (0, row_data_utils_1.rowDataToNumber)(v.weight),
2139
- })),
2140
- }));
2141
- }
2142
- /**
2143
- * Materialize the final query contained within this loaded `Model`.
2144
- *
2145
- * @return A promise to a prepared query.
2146
- */
2147
- getFinalQuery() {
2148
- return this.loadFinalQuery().getPreparedQuery();
2149
- }
2150
- /**
2151
- * Materialize an unnamed query contained within this loaded `Model` by index.
2152
- *
2153
- * @param index The index of the query contained within this loaded `Model`.
2154
- * @return A promise to a prepared query.
2155
- */
2156
- getQueryByIndex(index) {
2157
- return this.loadQueryByIndex(index).getPreparedQuery();
2158
- }
2159
- /**
2160
- * Materialize a query contained within this loaded `Model` by name.
2161
- *
2162
- * @param name The name of the query contained within this loaded `Model`.
2163
- * @return A promise to a prepared query.
2164
- */
2165
- getQueryByName(name) {
2166
- return this.loadQueryByName(name).getPreparedQuery();
2167
- }
2168
- /**
2169
- * Materialize a query against this loaded `Model` by its URL or contents.
2170
- *
2171
- * @param query The URL or contents of a query document to compile.
2172
- * @return A promise to a prepared query.
2173
- */
2174
- getQuery(query, options) {
2175
- return this.loadQuery(query, options).getPreparedQuery();
2176
- }
2177
- // TODO Consider formalizing this. Perhaps as a `withQuery` method,
2178
- // as well as a `PreparedQuery.fromQueryDefinition` if we choose to expose
2179
- // `InternalQuery` to the world formally. For now, this should only
2180
- // be used in tests.
2181
- _loadQueryFromQueryDef(query, options) {
2182
- return this.makeQueryMaterializer(async () => {
2183
- const model = await this.materialize();
2184
- return new PreparedQuery(query, model._modelDef, model.problems);
2185
- }, {
2186
- ...this.compileQueryOptions,
2187
- ...options,
2188
- });
2189
- }
2190
- /**
2191
- * Load an explore contained within this loaded `Model` by name.
2192
- *
2193
- * @param name The name of the explore contained within this loaded `Model`.
2194
- * @return An `ExploreMaterializer` capable of materializing the requested explore,
2195
- * or loading further related objects.
2196
- */
2197
- loadExploreByName(name) {
2198
- return this.makeExploreMaterializer(async () => {
2199
- return (await this.materialize()).getExploreByName(name);
2200
- }, this.compileQueryOptions);
2201
- }
2202
- /**
2203
- * Materialize an explore contained within this loaded `Model` by its name.
2204
- *
2205
- * @param query The name of an explore within this loaded `Model`.
2206
- * @return A promise to an explore.
2207
- */
2208
- getExploreByName(name) {
2209
- return this.loadExploreByName(name).getExplore();
2210
- }
2211
- /**
2212
- * Compile and materialize this loaded `Model`.
2213
- *
2214
- * @return A promise to the compiled model that is loaded.
2215
- */
2216
- getModel() {
2217
- return this.materialize();
2218
- }
2219
- }
2220
- exports.ModelMaterializer = ModelMaterializer;
2221
- /**
2222
- * An object representing the task of loading a `Query`, capable of
2223
- * materializing the query (via `getPreparedQuery()`) or extending the task to load
2224
- * prepared results or run the query (via e.g. `loadPreparedResult()` or `run()`).
2225
- */
2226
- class QueryMaterializer extends FluentState {
2227
- constructor(runtime, materialize, options) {
2228
- super(runtime, materialize);
2229
- this.runtime = runtime;
2230
- this.compileQueryOptions = options;
2231
- }
2232
- /**
2233
- * Run this loaded `Query`.
2234
- *
2235
- * @return The query results from running this loaded query.
2236
- */
2237
- async run(options) {
2238
- const connections = this.runtime.connections;
2239
- const preparedResult = await this.getPreparedResult(options);
2240
- const finalOptions = runSQLOptionsWithAnnotations(preparedResult, options);
2241
- return Malloy.run({ connections, preparedResult, options: finalOptions });
2242
- }
2243
- async *runStream(options) {
2244
- const preparedResult = await this.getPreparedResult(options);
2245
- const connections = this.runtime.connections;
2246
- const finalOptions = runSQLOptionsWithAnnotations(preparedResult, options);
2247
- const stream = Malloy.runStream({
2248
- connections,
2249
- preparedResult,
2250
- options: finalOptions,
2251
- });
2252
- for await (const row of stream) {
2253
- yield row;
2254
- }
2255
- }
2256
- /**
2257
- * Load the prepared result of this loaded query.
2258
- *
2259
- * @return A `PreparedResultMaterializer` capable of materializing the requested
2260
- * prepared query or running it.
2261
- */
2262
- loadPreparedResult(options) {
2263
- return this.makePreparedResultMaterializer(async () => {
2264
- return (await this.materialize()).getPreparedResult({
2265
- eventStream: this.eventStream,
2266
- ...this.compileQueryOptions,
2267
- ...options,
2268
- });
2269
- });
2270
- }
2271
- /**
2272
- * Materialize the prepared result of this loaded query.
2273
- *
2274
- * @return A promise of the prepared result of this loaded query.
2275
- */
2276
- getPreparedResult(options) {
2277
- return this.loadPreparedResult(options).getPreparedResult();
2278
- }
2279
- /**
2280
- * Materialize the SQL of this loaded query.
2281
- *
2282
- * @return A promise of the SQL string.
2283
- */
2284
- async getSQL(options) {
2285
- return (await this.getPreparedResult(options)).sql;
2286
- }
2287
- /**
2288
- * Materialize this loaded query.
2289
- *
2290
- * @return A promise of the `PreparedQuery`.
2291
- */
2292
- getPreparedQuery() {
2293
- return this.materialize();
2294
- }
2295
- /**
2296
- * Estimates the cost of this loaded `Query`.
2297
- *
2298
- * @return The estimated cost of running this loaded query.
2299
- */
2300
- async estimateQueryCost(options) {
2301
- const connections = this.runtime.connections;
2302
- const preparedResult = await this.getPreparedResult(options);
2303
- return Malloy.estimateQueryCost({ connections, preparedResult });
2304
- }
2305
- get eventStream() {
2306
- return this.runtime.eventStream;
2307
- }
2308
- }
2309
- exports.QueryMaterializer = QueryMaterializer;
2310
- function runSQLOptionsWithAnnotations(preparedResult, givenOptions) {
2311
- return {
2312
- queryAnnotation: preparedResult.annotation,
2313
- modelAnnotation: preparedResult.modelAnnotation,
2314
- ...givenOptions,
2315
- };
2316
- }
2317
- /**
2318
- * An object representing the task of loading a `PreparedResult`, capable of
2319
- * materializing the prepared result (via `getPreparedResult()`) or extending the task run
2320
- * the query.
2321
- */
2322
- class PreparedResultMaterializer extends FluentState {
2323
- /**
2324
- * Run this prepared result.
2325
- *
2326
- * @return A promise to the query result data.
2327
- */
2328
- async run(options) {
2329
- const preparedResult = await this.getPreparedResult();
2330
- const connections = this.runtime.connections;
2331
- const finalOptions = runSQLOptionsWithAnnotations(preparedResult, options);
2332
- return Malloy.run({
2333
- connections,
2334
- preparedResult,
2335
- options: finalOptions,
2336
- });
2337
- }
2338
- async *runStream(options) {
2339
- const preparedResult = await this.getPreparedResult();
2340
- const connections = this.runtime.connections;
2341
- const finalOptions = runSQLOptionsWithAnnotations(preparedResult, options);
2342
- const stream = Malloy.runStream({
2343
- connections,
2344
- preparedResult,
2345
- options: finalOptions,
2346
- });
2347
- for await (const row of stream) {
2348
- yield row;
2349
- }
2350
- }
2351
- /**
2352
- * Materialize this loaded prepared result.
2353
- *
2354
- * @return A promise of a prepared result.
2355
- */
2356
- getPreparedResult() {
2357
- return this.materialize();
2358
- }
2359
- /**
2360
- * Materialize the SQL of this loaded prepared result.
2361
- *
2362
- * @return A promise to the SQL string.
2363
- */
2364
- async getSQL() {
2365
- return (await this.getPreparedResult()).sql;
2366
- }
2367
- }
2368
- exports.PreparedResultMaterializer = PreparedResultMaterializer;
2369
- /**
2370
- * An object representing the task of loading an `Explore`, capable of
2371
- * materializing the explore (via `getExplore()`) or extending the task to produce
2372
- * related queries.
2373
- */
2374
- class ExploreMaterializer extends FluentState {
2375
- constructor(runtime, materialize, options) {
2376
- var _a;
2377
- super(runtime, materialize);
2378
- this.runtime = runtime;
2379
- this.replaceMaterializedReferences =
2380
- (_a = options === null || options === void 0 ? void 0 : options.replaceMaterializedReferences) !== null && _a !== void 0 ? _a : false;
2381
- }
2382
- /**
2383
- * Load a query contained within this loaded explore.
2384
- *
2385
- * @param name The name of the query to load.
2386
- * @return A `QueryMaterializer` capable of materializing the requested query, running it,
2387
- * or loading further related objects.
2388
- */
2389
- loadQueryByName(name, options) {
2390
- return this.makeQueryMaterializer(async () => {
2391
- return (await this.materialize()).getQueryByName(name);
2392
- }, options);
2393
- }
2394
- /**
2395
- * Materialize a query contained within this loaded explore.
2396
- *
2397
- * @param name The name of the query to materialize.
2398
- * @return A promise to the requested prepared query.
2399
- */
2400
- getQueryByName(name) {
2401
- return this.loadQueryByName(name).getPreparedQuery();
2402
- }
2403
- /**
2404
- * Materialize this loaded explore.
2405
- *
2406
- * @return A promise to the compiled `Explore`.
2407
- */
2408
- getExplore() {
2409
- return this.materialize();
2410
- }
2411
- }
2412
- exports.ExploreMaterializer = ExploreMaterializer;
2413
- /**
2414
- * The result of running a Malloy query.
2415
- *
2416
- * A `Result` is a `PreparedResult` along with the data retrieved from running the query.
2417
- */
2418
- class Result extends PreparedResult {
2419
- constructor(queryResult, modelDef) {
2420
- super(queryResult, modelDef);
2421
- this.inner = queryResult;
2422
- }
2423
- get _queryResult() {
2424
- return this.inner;
2425
- }
2426
- /**
2427
- * @return The result data.
2428
- */
2429
- get data() {
2430
- return new DataArray(this.inner.result, this.resultExplore, undefined, undefined);
2431
- }
2432
- get totalRows() {
2433
- return this.inner.totalRows;
2434
- }
2435
- get runStats() {
2436
- return this.inner.runStats;
2437
- }
2438
- get profilingUrl() {
2439
- return this.inner.profilingUrl;
2440
- }
2441
- toJSON() {
2442
- // DDL statements (INSTALL, LOAD, CREATE SECRET, etc.) don't have a schema,
2443
- // so we can't call this.data.toJSON() which requires resultExplore.
2444
- if (!this.hasSchema) {
2445
- return {
2446
- queryResult: this.inner,
2447
- modelDef: this._modelDef,
2448
- };
2449
- }
2450
- // The result rows are converted to JSON separately because they
2451
- // may contain un-serializable data types.
2452
- return {
2453
- queryResult: { ...this.inner, result: this.data.toJSON() },
2454
- modelDef: this._modelDef,
2455
- };
2456
- }
2457
- static fromJSON({ queryResult, modelDef }) {
2458
- return new Result(queryResult, modelDef);
2459
- }
2460
- }
2461
- exports.Result = Result;
2462
- class Data {
2463
- constructor(field, parent, parentRecord) {
2464
- this.parent = parent;
2465
- this.parentRecord = parentRecord;
2466
- this._field = field;
2467
- }
2468
- get field() {
2469
- return this._field;
2470
- }
2471
- isString() {
2472
- return this instanceof DataString;
2473
- }
2474
- get string() {
2475
- if (this.isString()) {
2476
- return this;
2477
- }
2478
- throw new Error('Not a string.');
2479
- }
2480
- isBoolean() {
2481
- return this instanceof DataBoolean;
2482
- }
2483
- get boolean() {
2484
- if (this.isBoolean()) {
2485
- return this;
2486
- }
2487
- throw new Error('Not a boolean.');
2488
- }
2489
- isNumber() {
2490
- return this instanceof DataNumber;
2491
- }
2492
- get number() {
2493
- if (this.isNumber()) {
2494
- return this;
2495
- }
2496
- throw new Error('Not a number.');
2497
- }
2498
- isTimestamp() {
2499
- return this instanceof DataTimestamp;
2500
- }
2501
- get timestamp() {
2502
- if (this.isTimestamp()) {
2503
- return this;
2504
- }
2505
- throw new Error('Not a timestamp.');
2506
- }
2507
- isDate() {
2508
- return this instanceof DataDate;
2509
- }
2510
- get date() {
2511
- if (this.isDate()) {
2512
- return this;
2513
- }
2514
- throw new Error('Not a date.');
2515
- }
2516
- isNull() {
2517
- return this instanceof DataNull;
2518
- }
2519
- isBytes() {
2520
- return this instanceof DataBytes;
2521
- }
2522
- get bytes() {
2523
- if (this.isBytes()) {
2524
- return this;
2525
- }
2526
- throw new Error('Not bytes.');
2527
- }
2528
- isRecord() {
2529
- return this instanceof DataRecord;
2530
- }
2531
- get record() {
2532
- if (this.isRecord()) {
2533
- return this;
2534
- }
2535
- throw new Error('Not a record.');
2536
- }
2537
- isUnsupported() {
2538
- return this instanceof DataUnsupported;
2539
- }
2540
- get unsupported() {
2541
- if (this.isUnsupported()) {
2542
- return this;
2543
- }
2544
- throw new Error('Not unsupported.');
2545
- }
2546
- isArray() {
2547
- return this instanceof DataArray;
2548
- }
2549
- get array() {
2550
- if (this.isArray()) {
2551
- return this;
2552
- }
2553
- throw new Error('Not an array.');
2554
- }
2555
- isArrayOrRecord() {
2556
- if (this instanceof DataArray || this instanceof DataRecord) {
2557
- return this;
2558
- }
2559
- throw new Error('No Array or Record');
2560
- }
2561
- isScalar() {
2562
- return true;
2563
- }
2564
- }
2565
- class ScalarData extends Data {
2566
- constructor(value, field, parent, parentRecord) {
2567
- super(field, parent, parentRecord);
2568
- this._value = value;
2569
- this._field = field;
2570
- }
2571
- get value() {
2572
- return this._value;
2573
- }
2574
- get field() {
2575
- return this._field;
2576
- }
2577
- isScalar() {
2578
- return this instanceof ScalarData;
2579
- }
2580
- }
2581
- class DataString extends ScalarData {
2582
- constructor(value, field, parent, parentRecord) {
2583
- super(value, field, parent, parentRecord);
2584
- this._field = field;
2585
- }
2586
- get field() {
2587
- return this._field;
2588
- }
2589
- get key() {
2590
- return this.value;
2591
- }
2592
- compareTo(other) {
2593
- return this.value
2594
- .toLocaleLowerCase()
2595
- .localeCompare(other.value.toLocaleLowerCase());
2596
- }
2597
- }
2598
- class DataUnsupported extends ScalarData {
2599
- constructor(value, field, parent, parentRecord) {
2600
- super(value, field, parent, parentRecord);
2601
- this._field = field;
2602
- }
2603
- get field() {
2604
- return this._field;
2605
- }
2606
- get key() {
2607
- return '<unsupported>';
2608
- }
2609
- compareTo(_other) {
2610
- return 0;
2611
- }
2612
- }
2613
- class DataBoolean extends ScalarData {
2614
- constructor(value, field, parent, parentRecord) {
2615
- super(value, field, parent, parentRecord);
2616
- this._field = field;
2617
- }
2618
- get field() {
2619
- return this._field;
2620
- }
2621
- get key() {
2622
- return `${this.value}`;
2623
- }
2624
- compareTo(other) {
2625
- if (this.value === other.value) {
2626
- return 0;
2627
- }
2628
- if (this.value) {
2629
- return 1;
2630
- }
2631
- return -1;
2632
- }
2633
- }
2634
- class DataJSON extends ScalarData {
2635
- constructor(value, field, parent, parentRecord) {
2636
- super(value, field, parent, parentRecord);
2637
- this._field = field;
2638
- }
2639
- get field() {
2640
- return this._field;
2641
- }
2642
- get key() {
2643
- return this.value;
2644
- }
2645
- compareTo(other) {
2646
- const value = this.value.toString();
2647
- const otherValue = other.toString();
2648
- if (value === otherValue) {
2649
- return 0;
2650
- }
2651
- else if (value > otherValue) {
2652
- return 1;
2653
- }
2654
- else {
2655
- return -1;
2656
- }
2657
- }
2658
- }
2659
- class DataNumber extends ScalarData {
2660
- constructor(value, field, parent, parentRecord) {
2661
- super((0, row_data_utils_1.rowDataToNumber)(value), field, parent, parentRecord);
2662
- this._field = field;
2663
- }
2664
- get field() {
2665
- return this._field;
2666
- }
2667
- get key() {
2668
- return `${this.value}`;
2669
- }
2670
- compareTo(other) {
2671
- const difference = this.value - other.value;
2672
- if (difference > 0) {
2673
- return 1;
2674
- }
2675
- else if (difference === 0) {
2676
- return 0;
2677
- }
2678
- return -1;
2679
- }
2680
- }
2681
- class DataTimestamp extends ScalarData {
2682
- constructor(value, field, parent, parentRecord) {
2683
- super(value, field, parent, parentRecord);
2684
- this._field = field;
2685
- }
2686
- get value() {
2687
- return (0, row_data_utils_1.rowDataToDate)(this._value);
2688
- }
2689
- get field() {
2690
- return this._field;
2691
- }
2692
- get key() {
2693
- return `${this.value.toLocaleString()}`;
2694
- }
2695
- compareTo(other) {
2696
- if (this.value > other.value) {
2697
- return 1;
2698
- }
2699
- else if (this.value < other.value) {
2700
- return -1;
2701
- }
2702
- return 0;
2703
- }
2704
- }
2705
- class DataDate extends ScalarData {
2706
- constructor(value, field, parent, parentRecord) {
2707
- super(value, field, parent, parentRecord);
2708
- this._field = field;
2709
- }
2710
- get value() {
2711
- return (0, row_data_utils_1.rowDataToDate)(this._value);
2712
- }
2713
- get field() {
2714
- return this._field;
2715
- }
2716
- get key() {
2717
- return `${this.value.toLocaleString()}`;
2718
- }
2719
- compareTo(other) {
2720
- if (this.value > other.value) {
2721
- return 1;
2722
- }
2723
- else if (this.value < other.value) {
2724
- return -1;
2725
- }
2726
- return 0;
2727
- }
2728
- }
2729
- class DataBytes extends ScalarData {
2730
- get key() {
2731
- return this.value.toString();
2732
- }
2733
- compareTo(other) {
2734
- const value = this.value.toString();
2735
- const otherValue = other.toString();
2736
- if (value === otherValue) {
2737
- return 0;
2738
- }
2739
- else if (value > otherValue) {
2740
- return 1;
2741
- }
2742
- else {
2743
- return -1;
2744
- }
2745
- }
2746
- }
2747
- class DataNull extends Data {
2748
- get value() {
2749
- return null;
2750
- }
2751
- get key() {
2752
- return '<null>';
2753
- }
2754
- }
2755
- /**
2756
- * Safe bigint conversion - handles floats that are incorrectly typed as bigint
2757
- * (e.g., avg() results which should be float but Malloy marks as bigint).
2758
- */
2759
- function safeRowDataToBigint(value) {
2760
- const strValue = (0, row_data_utils_1.rowDataToSerializedBigint)(value);
2761
- if (strValue.includes('.') ||
2762
- strValue.includes('e') ||
2763
- strValue.includes('E')) {
2764
- return (0, row_data_utils_1.rowDataToNumber)(value);
2765
- }
2766
- try {
2767
- return BigInt(strValue);
2768
- }
2769
- catch {
2770
- return (0, row_data_utils_1.rowDataToNumber)(value);
2771
- }
2772
- }
2773
- /**
2774
- * Safe bigint serialization - returns number for floats that should stay as numbers.
2775
- */
2776
- function safeRowDataToSerializedBigint(value) {
2777
- const strValue = (0, row_data_utils_1.rowDataToSerializedBigint)(value);
2778
- if (strValue.includes('.') ||
2779
- strValue.includes('e') ||
2780
- strValue.includes('E')) {
2781
- return (0, row_data_utils_1.rowDataToNumber)(value);
2782
- }
2783
- return strValue;
2784
- }
2785
- /**
2786
- * Normalizers for toObject() - returns JS native types (number | bigint, Date)
2787
- */
2788
- const OBJECT_NORMALIZERS = {
2789
- number: row_data_utils_1.rowDataToNumber,
2790
- bigint: safeRowDataToBigint,
2791
- date: row_data_utils_1.rowDataToDate,
2792
- };
2793
- /**
2794
- * Normalizers for toJSON() - returns JSON-safe types (number | string, ISO strings)
2795
- */
2796
- const JSON_NORMALIZERS = {
2797
- number: row_data_utils_1.rowDataToNumber,
2798
- bigint: safeRowDataToSerializedBigint,
2799
- date: (value) => (0, row_data_utils_1.rowDataToDate)(value).toISOString(),
2800
- };
2801
- /**
2802
- * Walk a QueryData array and normalize values according to the given normalizers.
2803
- */
2804
- function walkQueryData(data, structDef, normalizers) {
2805
- return data.map(row => walkQueryDataRow(row, structDef, normalizers));
2806
- }
2807
- /**
2808
- * Walk a QueryDataRow and normalize values according to the given normalizers.
2809
- */
2810
- function walkQueryDataRow(row, structDef, normalizers) {
2811
- var _a;
2812
- const result = {};
2813
- for (const fieldDef of structDef.fields) {
2814
- const fieldName = (_a = fieldDef.as) !== null && _a !== void 0 ? _a : fieldDef.name;
2815
- const value = row[fieldName];
2816
- result[fieldName] = walkValue(value, fieldDef, normalizers);
2817
- }
2818
- return result;
2819
- }
2820
- /**
2821
- * Normalize a single value based on its field definition.
2822
- */
2823
- function walkValue(value, fieldDef, normalizers) {
2824
- if (value === null || value === undefined) {
2825
- return null;
2826
- }
2827
- // Handle scalar types
2828
- if (fieldDef.type === 'number') {
2829
- const numberDef = fieldDef;
2830
- if (numberDef.numberType === 'bigint') {
2831
- return normalizers.bigint(value);
2832
- }
2833
- return normalizers.number(value);
2834
- }
2835
- if (fieldDef.type === 'date' ||
2836
- fieldDef.type === 'timestamp' ||
2837
- fieldDef.type === 'timestamptz') {
2838
- return normalizers.date(value);
2839
- }
2840
- if (fieldDef.type === 'string' ||
2841
- fieldDef.type === 'boolean' ||
2842
- fieldDef.type === 'json' ||
2843
- fieldDef.type === 'sql native') {
2844
- // Pass through as-is (or with minimal conversion for booleans from numbers)
2845
- if (fieldDef.type === 'boolean' && typeof value === 'number') {
2846
- return value !== 0;
2847
- }
2848
- return value;
2849
- }
2850
- // Handle arrays
2851
- if (fieldDef.type === 'array') {
2852
- if (!Array.isArray(value)) {
2853
- return value; // Unexpected, but don't crash
2854
- }
2855
- if ((0, model_1.isRepeatedRecord)(fieldDef)) {
2856
- // Array of records - recurse into each record
2857
- return value.map(item => walkQueryDataRow(item, fieldDef, normalizers));
2858
- }
2859
- else if ((0, model_1.isBasicArray)(fieldDef)) {
2860
- // Scalar array - normalize each element based on elementTypeDef
2861
- // Cast needed because QueryValue type doesn't cleanly express scalar arrays
2862
- const elementType = fieldDef.elementTypeDef;
2863
- return value.map(item => walkScalarValue(item, elementType, normalizers));
2864
- }
2865
- }
2866
- // Handle records (non-array)
2867
- if (fieldDef.type === 'record') {
2868
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
2869
- return walkQueryDataRow(value, fieldDef, normalizers);
2870
- }
2871
- }
2872
- // Fallback - pass through
2873
- return value;
2874
- }
2875
- /**
2876
- * Normalize a scalar value (not in a row context, e.g., elements of a scalar array).
2877
- */
2878
- function walkScalarValue(value, typeDef, normalizers) {
2879
- if (value === null || value === undefined) {
2880
- return null;
2881
- }
2882
- if (typeDef.type === 'number') {
2883
- const numberDef = typeDef;
2884
- if (numberDef.numberType === 'bigint') {
2885
- return normalizers.bigint(value);
2886
- }
2887
- return normalizers.number(value);
2888
- }
2889
- if (typeDef.type === 'date' ||
2890
- typeDef.type === 'timestamp' ||
2891
- typeDef.type === 'timestamptz') {
2892
- return normalizers.date(value);
2893
- }
2894
- if (typeDef.type === 'boolean' && typeof value === 'number') {
2895
- return value !== 0;
2896
- }
2897
- // Handle nested arrays (array of arrays)
2898
- if (typeDef.type === 'array' && Array.isArray(value)) {
2899
- if ((0, model_1.isBasicArray)(typeDef)) {
2900
- const elementType = typeDef.elementTypeDef;
2901
- return value.map(item => walkScalarValue(item, elementType, normalizers));
2902
- }
2903
- else if ((0, model_1.isRepeatedRecord)(typeDef)) {
2904
- return value.map(item => walkQueryDataRow(item, typeDef, normalizers));
2905
- }
2906
- }
2907
- // Pass through other types
2908
- return value;
2909
- }
2910
- class DataArray extends Data {
2911
- constructor(queryData, field, parent, parentRecord) {
2912
- super(field, parent, parentRecord);
2913
- this.rowCache = new Map();
2914
- this.queryData = queryData;
2915
- this._field = field;
2916
- }
2917
- /**
2918
- * @return The `Explore` that describes the structure of this data.
2919
- */
2920
- get field() {
2921
- return this._field;
2922
- }
2923
- /**
2924
- * @return The raw query data as returned by the database driver.
2925
- * Values may be in various formats depending on the driver (wrapper objects, strings, etc.).
2926
- * Use this for passing to mapData() which handles normalization itself.
2927
- */
2928
- get rawData() {
2929
- return this.queryData;
2930
- }
2931
- /**
2932
- * @return Normalized data with JS native types (number | bigint, Date).
2933
- * Use this for CSV output, tests, and general programmatic access.
2934
- */
2935
- toObject() {
2936
- return walkQueryData(this.queryData, this._field.structDef, OBJECT_NORMALIZERS);
2937
- }
2938
- /**
2939
- * @return Normalized data with JSON-safe types (numbers as number | string, dates as ISO strings).
2940
- * Use this for JSON serialization.
2941
- */
2942
- toJSON() {
2943
- return walkQueryData(this.queryData, this._field.structDef, JSON_NORMALIZERS);
2944
- }
2945
- path(...path) {
2946
- return getPath(this, path);
2947
- }
2948
- row(index) {
2949
- let record = this.rowCache.get(index);
2950
- if (!record) {
2951
- record = new DataRecord(this.queryData[index], index, this.field, this, this.parentRecord);
2952
- this.rowCache.set(index, record);
2953
- }
2954
- return record;
2955
- }
2956
- get rowCount() {
2957
- return this.queryData.length;
2958
- }
2959
- get value() {
2960
- return this.toObject();
2961
- }
2962
- [Symbol.iterator]() {
2963
- let currentIndex = 0;
2964
- const queryData = this.queryData;
2965
- const getRow = (index) => this.row(index);
2966
- return {
2967
- next() {
2968
- if (currentIndex < queryData.length) {
2969
- return { value: getRow(currentIndex++), done: false };
2970
- }
2971
- else {
2972
- return { value: undefined, done: true };
2973
- }
2974
- },
2975
- };
2976
- }
2977
- async *inMemoryStream() {
2978
- for (let i = 0; i < this.queryData.length; i++) {
2979
- yield this.row(i);
2980
- }
2981
- }
2982
- }
2983
- exports.DataArray = DataArray;
2984
- function getPath(data, path) {
2985
- for (const segment of path) {
2986
- if (typeof segment === 'number') {
2987
- data = data.array.row(segment);
2988
- }
2989
- else {
2990
- data = data.record.cell(segment);
2991
- }
2992
- }
2993
- return data;
2994
- }
2995
- class DataRecord extends Data {
2996
- constructor(queryDataRow, index, field, parent, parentRecord) {
2997
- super(field, parent, parentRecord);
2998
- this.cellCache = new Map();
2999
- this.queryDataRow = queryDataRow;
3000
- this._field = field;
3001
- this.index = index;
3002
- }
3003
- /**
3004
- * @return Normalized data with JS native types (number | bigint, Date).
3005
- * Use this for CSV output, tests, and general programmatic access.
3006
- */
3007
- toObject() {
3008
- return walkQueryDataRow(this.queryDataRow, this._field.structDef, OBJECT_NORMALIZERS);
3009
- }
3010
- /**
3011
- * @return Normalized data with JSON-safe types (numbers as number | string, dates as ISO strings).
3012
- * Use this for JSON serialization.
3013
- */
3014
- toJSON() {
3015
- return walkQueryDataRow(this.queryDataRow, this._field.structDef, JSON_NORMALIZERS);
3016
- }
3017
- path(...path) {
3018
- return getPath(this, path);
3019
- }
3020
- cell(fieldOrName) {
3021
- const fieldName = typeof fieldOrName === 'string' ? fieldOrName : fieldOrName.name;
3022
- const field = this._field.getFieldByName(fieldName);
3023
- let column = this.cellCache.get(fieldName);
3024
- if (!column) {
3025
- const value = this.queryDataRow[fieldName];
3026
- if (value === null) {
3027
- column = new DataNull(field, this, this);
3028
- }
3029
- else if (field.isAtomicField()) {
3030
- if (field.isBoolean()) {
3031
- column = new DataBoolean(value, field, this, this);
3032
- }
3033
- else if (field.isDate()) {
3034
- column = new DataDate(value, field, this, this);
3035
- }
3036
- else if (field.isJSON()) {
3037
- column = new DataJSON(value, field, this, this);
3038
- }
3039
- else if (field.isTimestamp()) {
3040
- column = new DataTimestamp(value, field, this, this);
3041
- }
3042
- else if (field.isNumber()) {
3043
- column = new DataNumber(value, field, this, this);
3044
- }
3045
- else if (field.isString()) {
3046
- column = new DataString(value, field, this, this);
3047
- }
3048
- else if (field.isUnsupported()) {
3049
- column = new DataUnsupported(value, field, this, this);
3050
- }
3051
- }
3052
- else if (field.isExploreField()) {
3053
- if (Array.isArray(value)) {
3054
- column = new DataArray(value, field, this, this);
3055
- }
3056
- else {
3057
- column = new DataRecord(value, undefined, field, this, this);
3058
- }
3059
- }
3060
- if (column)
3061
- this.cellCache.set(fieldName, column);
3062
- }
3063
- if (column)
3064
- return column;
3065
- throw new Error(`Internal Error: could not construct data column for field '${fieldName}'.`);
3066
- }
3067
- get value() {
3068
- throw new Error('Not implemented;');
3069
- }
3070
- // Non repeating values show up as DataRecords
3071
- get field() {
3072
- return this._field;
3073
- }
3074
- // Allow iteration over non repeating values to simplify end user code.
3075
- [Symbol.iterator]() {
3076
- let returned = false;
3077
- const getSelf = () => {
3078
- return this;
3079
- };
3080
- return {
3081
- next() {
3082
- if (!returned) {
3083
- returned = true;
3084
- return {
3085
- value: getSelf(),
3086
- done: false,
3087
- };
3088
- }
3089
- else {
3090
- return { value: undefined, done: true };
3091
- }
3092
- },
3093
- };
3094
- }
3095
- }
3096
- exports.DataRecord = DataRecord;
3097
- class DataWriter {
3098
- constructor(stream) {
3099
- this.stream = stream;
3100
- }
3101
- }
3102
- exports.DataWriter = DataWriter;
3103
- class JSONWriter extends DataWriter {
3104
- async process(data) {
3105
- this.stream.write('[\n');
3106
- for await (const row of data) {
3107
- if (row.index !== undefined && row.index > 0) {
3108
- this.stream.write(',\n');
3109
- }
3110
- // toJSON() returns JSON-safe values: bigints as strings, dates as ISO strings
3111
- const json = JSON.stringify(row.toJSON(), null, 2);
3112
- const jsonLines = json.split('\n');
3113
- for (let i = 0; i < jsonLines.length; i++) {
3114
- const line = jsonLines[i];
3115
- this.stream.write(` ${line}`);
3116
- if (i < jsonLines.length - 1) {
3117
- this.stream.write('\n');
3118
- }
3119
- }
3120
- }
3121
- this.stream.write('\n]\n');
3122
- this.stream.close();
3123
- }
3124
- }
3125
- exports.JSONWriter = JSONWriter;
3126
- /**
3127
- * CSV writer class that handles nested data.
3128
- * This writer creates CSV using a DFS traversal of the result dataset.
3129
- * Each trivial column value is converted to a CSV of 1x1 matrix and all the
3130
- * columns are merged together to create a CSV that represents 1 QueryDataRow.
3131
- * Since this follows DFS, each non trivial data is rendered into a NxM matrix
3132
- * where N is the number of rows in the nested data and M is the number of
3133
- * columns it has.
3134
- * For any row with X number of columns, we end up with X number of NxM matrices
3135
- * where the value of N,M pair may be different for each column.
3136
- * We then merge the matrices so that we end up with a larger matrix of size
3137
- * Max(N)xSum(M) by taking one row of csv from each matric at a time. For any
3138
- * matrix with N<Max(N), we add a row of empty CSV cells of size N.
3139
- */
3140
- class CSVWriter extends DataWriter {
3141
- constructor() {
3142
- super(...arguments);
3143
- this.columnSeparator = ',';
3144
- this.rowSeparator = '\n';
3145
- this.quoteCharacter = '"';
3146
- this.includeHeader = true;
3147
- this.emptyCell = '';
3148
- }
3149
- escape(value) {
3150
- const hasInnerQuote = value.includes(this.quoteCharacter);
3151
- const hasInnerCommas = value.includes(this.columnSeparator);
3152
- const hasNewline = value.includes(this.rowSeparator);
3153
- const needsQuoting = hasInnerCommas || hasInnerQuote || hasNewline;
3154
- if (hasInnerQuote) {
3155
- value = value.replace(new RegExp(this.quoteCharacter, 'g'), this.quoteCharacter + this.quoteCharacter);
3156
- }
3157
- if (needsQuoting) {
3158
- value = this.quoteCharacter + value + this.quoteCharacter;
3159
- }
3160
- return value;
3161
- }
3162
- // Re-using the old stringify method for sanity.
3163
- stringify(value) {
3164
- if (value === null) {
3165
- return this.emptyCell;
3166
- }
3167
- else if (value instanceof Date) {
3168
- return value.toISOString();
3169
- }
3170
- else if (typeof value === 'boolean' || typeof value === 'number') {
3171
- return JSON.stringify(value);
3172
- }
3173
- else if (typeof value === 'bigint') {
3174
- // Bigints from toObject() - write as unquoted number string
3175
- return value.toString();
3176
- }
3177
- else {
3178
- return `${value}`;
3179
- }
3180
- }
3181
- // Extra weight to be added becase of nested tables inside the cells.
3182
- getColWeight(jsonVal) {
3183
- let firstVal = jsonVal;
3184
- if (Array.isArray(jsonVal)) {
3185
- firstVal = jsonVal[0];
3186
- }
3187
- let numKeys = 0;
3188
- for (const key in firstVal) {
3189
- numKeys = numKeys + 1;
3190
- const val = firstVal[key];
3191
- if (Array.isArray(val)) {
3192
- const weight = this.getColWeight(val) - 1;
3193
- numKeys = numKeys + weight;
3194
- }
3195
- }
3196
- return numKeys;
3197
- }
3198
- // Get header row along with extra empty spaces for nested children.
3199
- getHeaderRow(row) {
3200
- const csv = [];
3201
- let width = 0;
3202
- for (const key in row) {
3203
- csv.push(this.escape(key));
3204
- const val = row[key];
3205
- width++;
3206
- if (Array.isArray(val)) {
3207
- const numKeys = this.getColWeight(val) - 1;
3208
- width = width + numKeys;
3209
- for (let i = 0; i < numKeys; i++) {
3210
- csv.push(this.emptyCell);
3211
- }
3212
- }
3213
- }
3214
- return { rows: [csv.join(this.columnSeparator)], length: 1, width: width };
3215
- }
3216
- // Merge the child matrices i.e. merge the columns into one bigger matrix i.e. CSV.
3217
- mergeMatrices(matrices) {
3218
- const maxLength = Math.max(...matrices.map(matrix => matrix.length));
3219
- const matrixWidth = matrices.reduce((sum, matrix) => sum + matrix.width, 0);
3220
- const csvMatrix = [];
3221
- for (let i = 0; i < maxLength; i++) {
3222
- const csvRow = [];
3223
- for (const matrix of matrices) {
3224
- if (i < matrix.length) {
3225
- csvRow.push(matrix.rows[i]);
3226
- }
3227
- else {
3228
- // Add empty cells.
3229
- const emptyCells = Array(matrix.width).fill(this.emptyCell);
3230
- csvRow.push(...emptyCells);
3231
- }
3232
- }
3233
- csvMatrix.push(csvRow.join(this.columnSeparator));
3234
- }
3235
- return {
3236
- rows: csvMatrix,
3237
- length: maxLength,
3238
- width: matrixWidth,
3239
- };
3240
- }
3241
- // Gets CSV for a data cell that has nested data.
3242
- getChildMatrix(jsonVal) {
3243
- // This is not expected to happen.
3244
- if (!Array.isArray(jsonVal)) {
3245
- return {
3246
- rows: ['Invalid data found, value is not an array'],
3247
- length: 1,
3248
- width: 1,
3249
- };
3250
- }
3251
- else if (jsonVal.length === 0) {
3252
- return {
3253
- rows: [''],
3254
- length: 1,
3255
- width: 1,
3256
- };
3257
- }
3258
- const csvMatrix = [];
3259
- const header = this.getHeaderRow(jsonVal[0]);
3260
- // Header has 1 row.
3261
- csvMatrix.push(...header.rows);
3262
- const width = header.width;
3263
- let rowCount = 1;
3264
- for (const row of jsonVal) {
3265
- const rowMatrix = this.getRowMatrix(row);
3266
- rowCount = rowCount + rowMatrix.length;
3267
- csvMatrix.push(...rowMatrix.rows);
3268
- }
3269
- return { rows: csvMatrix, length: rowCount, width: width };
3270
- }
3271
- // Creates CSV content for one row of data.
3272
- getRowMatrix(row) {
3273
- const matrices = [];
3274
- for (const key in row) {
3275
- const val = row[key];
3276
- if (!Array.isArray(val)) {
3277
- const cell = {
3278
- rows: [this.stringify(val)],
3279
- length: 1,
3280
- width: 1,
3281
- };
3282
- matrices.push(cell);
3283
- }
3284
- else {
3285
- const cell = this.getChildMatrix(val);
3286
- matrices.push(cell);
3287
- }
3288
- }
3289
- return this.mergeMatrices(matrices);
3290
- }
3291
- async process(data) {
3292
- let headerDefined = false;
3293
- for await (const row of data) {
3294
- if (!headerDefined && this.includeHeader) {
3295
- const header = this.getHeaderRow(row.toObject());
3296
- this.stream.write(header.rows[0]);
3297
- this.stream.write(this.rowSeparator);
3298
- headerDefined = true;
3299
- }
3300
- const rowCsv = this.getRowMatrix(row.toObject());
3301
- for (const line of rowCsv.rows) {
3302
- this.stream.write(line);
3303
- this.stream.write(this.rowSeparator);
3304
- }
3305
- }
3306
- this.stream.close();
3307
- }
3308
- }
3309
- exports.CSVWriter = CSVWriter;
3310
- class CacheManager {
3311
- constructor(modelCache) {
3312
- this.modelCache = modelCache;
3313
- this.modelDependencies = new Map();
3314
- this.modelInvalidationKeys = new Map();
3315
- }
3316
- async getCachedModelDef(urlReader, url) {
3317
- const _dependencies = this.modelDependencies.get(url);
3318
- if (_dependencies === undefined) {
3319
- return undefined;
3320
- }
3321
- const dependencies = [url, ...flatDeps(_dependencies)];
3322
- const invalidationKeys = {};
3323
- for (const dependency of dependencies) {
3324
- const invalidationKey = this.modelInvalidationKeys.get(dependency);
3325
- if (invalidationKey === undefined || invalidationKey === null) {
3326
- return undefined;
3327
- }
3328
- invalidationKeys[dependency] = invalidationKey;
3329
- }
3330
- for (const dependency of dependencies) {
3331
- const invalidationKey = await getInvalidationKey(urlReader, new URL(dependency));
3332
- if (invalidationKey !== invalidationKeys[dependency]) {
3333
- return undefined;
3334
- }
3335
- }
3336
- const cached = await this.modelCache.getModel(new URL(url));
3337
- if (cached === undefined) {
3338
- return undefined;
3339
- }
3340
- for (const dependency of dependencies) {
3341
- if (cached.invalidationKeys[dependency] !== invalidationKeys[dependency]) {
3342
- return undefined;
3343
- }
3344
- }
3345
- // Return the cached model def and the invalidation keys for this
3346
- // model def's dependencies
3347
- return { modelDef: cached.modelDef, invalidationKeys };
3348
- }
3349
- async setCachedModelDef(url, cachedModel) {
3350
- this.modelDependencies.set(url, cachedModel.modelDef.dependencies);
3351
- const invalidationKeys = {};
3352
- for (const dependency of [
3353
- url,
3354
- ...flatDeps(cachedModel.modelDef.dependencies),
3355
- ]) {
3356
- if (cachedModel.invalidationKeys[dependency] === null) {
3357
- return false;
3358
- }
3359
- if (cachedModel.invalidationKeys[dependency] === undefined) {
3360
- throw new Error(`Missing invalidation key for dependency ${dependency}`);
3361
- }
3362
- this.modelInvalidationKeys.set(dependency, cachedModel.invalidationKeys[dependency]);
3363
- invalidationKeys[dependency] = cachedModel.invalidationKeys[dependency];
3364
- }
3365
- const result = await this.modelCache.setModel(new URL(url), {
3366
- modelDef: cachedModel.modelDef,
3367
- invalidationKeys,
3368
- });
3369
- if (result) {
3370
- return true; // TODO just return `result` when it's a boolean
3371
- }
3372
- return false;
3373
- }
3374
- }
3375
- exports.CacheManager = CacheManager;
3376
- function flatDeps(tree) {
3377
- return [...Object.keys(tree), ...Object.values(tree).map(flatDeps).flat()];
3378
- }
3379
- // TODO maybe make this memory bounded....
3380
- class InMemoryModelCache {
3381
- constructor() {
3382
- this.models = new Map();
3383
- }
3384
- async getModel(url) {
3385
- return Promise.resolve(this.models.get(url.toString()));
3386
- }
3387
- async setModel(url, cachedModel) {
3388
- this.models.set(url.toString(), cachedModel);
3389
- return Promise.resolve(true);
3390
- }
3391
- }
3392
- exports.InMemoryModelCache = InMemoryModelCache;
3393
- function hashForInvalidationKey(input) {
3394
- const MALLOY_UUID = '76c17e9d-f3ce-5f2d-bfde-98ad3d2a37f6';
3395
- return (0, uuid_1.v5)(input, MALLOY_UUID);
3396
- }
3397
- async function readURL(urlReader, url) {
3398
- const result = await urlReader.readURL(url);
3399
- const { contents, invalidationKey } = typeof result === 'string'
3400
- ? { contents: result, invalidationKey: undefined }
3401
- : result;
3402
- return {
3403
- contents,
3404
- invalidationKey: isInternalURL(url.toString())
3405
- ? null
3406
- : invalidationKey !== null && invalidationKey !== void 0 ? invalidationKey : hashForInvalidationKey(contents),
3407
- };
3408
- }
3409
- async function getInvalidationKey(urlReader, url) {
3410
- if (isInternalURL(url.toString())) {
3411
- return null;
3412
- }
3413
- if (urlReader.getInvalidationKey !== undefined) {
3414
- return await urlReader.getInvalidationKey(url);
3415
- }
3416
- return (await readURL(urlReader, url)).invalidationKey;
3417
- }
3418
- function isInternalURL(url) {
3419
- return url.startsWith('internal://');
3420
- }
3421
- //# sourceMappingURL=malloy.js.map