@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
@@ -23,7 +23,7 @@
23
23
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
24
  */
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.aTableDef = void 0;
26
+ exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.mockSchema = exports.bqTableDef = exports.aTableDef = exports.TEST_DIALECT = void 0;
27
27
  exports.pretty = pretty;
28
28
  exports.humanify = humanify;
29
29
  exports.getExplore = getExplore;
@@ -134,65 +134,85 @@ function humanify(value) {
134
134
  }
135
135
  const intType = { type: 'number', numberType: 'integer' };
136
136
  const bigintType = { type: 'number', numberType: 'bigint' };
137
- const mockSchema = {
138
- 'aTable': {
139
- type: 'table',
140
- name: 'aTable',
141
- dialect: 'standardsql',
142
- tablePath: 'aTable',
143
- connection: 'test',
137
+ // Base fields shared by both duckdb and BigQuery table definitions
138
+ const baseFields = [
139
+ { type: 'string', name: 'astr' },
140
+ { type: 'number', name: 'af', numberType: 'float' },
141
+ { ...intType, name: 'ai' },
142
+ { ...bigintType, name: 'abig' },
143
+ { type: 'date', name: 'ad' },
144
+ { type: 'boolean', name: 'abool' },
145
+ { type: 'timestamp', name: 'ats' },
146
+ { type: 'sql native', name: 'aun' },
147
+ { type: 'sql native', name: 'aweird', rawType: 'weird' },
148
+ {
149
+ type: 'array',
150
+ name: 'astruct',
151
+ elementTypeDef: { type: 'record_element' },
152
+ join: 'many',
144
153
  fields: [
145
- { type: 'string', name: 'astr' },
146
- { type: 'number', name: 'af', numberType: 'float' },
147
- { ...intType, name: 'ai' },
148
- { ...bigintType, name: 'abig' },
149
- { type: 'date', name: 'ad' },
150
- { type: 'boolean', name: 'abool' },
151
- { type: 'timestamp', name: 'ats' },
152
- { type: 'sql native', name: 'aun' },
153
- { type: 'sql native', name: 'aweird', rawType: 'weird' },
154
- {
155
- type: 'array',
156
- name: 'astruct',
157
- elementTypeDef: { type: 'record_element' },
158
- join: 'many',
159
- fields: [
160
- {
161
- name: 'column',
162
- type: 'number',
163
- numberType: 'integer',
164
- },
165
- ],
166
- },
167
154
  {
168
- type: 'record',
169
- name: 'aninline',
170
- fields: [{ ...intType, name: 'column' }],
171
- join: 'one',
172
- matrixOperation: 'left',
155
+ name: 'column',
156
+ type: 'number',
157
+ numberType: 'integer',
173
158
  },
174
- (0, malloy_types_1.mkArrayDef)(intType, 'ais'),
175
159
  ],
176
160
  },
177
- 'malloytest.carriers': {
161
+ {
162
+ type: 'record',
163
+ name: 'aninline',
164
+ fields: [{ ...intType, name: 'column' }],
165
+ join: 'one',
166
+ matrixOperation: 'left',
167
+ },
168
+ (0, malloy_types_1.mkArrayDef)(intType, 'ais'),
169
+ ];
170
+ exports.TEST_DIALECT = 'duckdb';
171
+ exports.aTableDef = {
172
+ type: 'table',
173
+ name: 'aTable',
174
+ dialect: exports.TEST_DIALECT,
175
+ tablePath: 'aTable',
176
+ connection: '_db_',
177
+ fields: [
178
+ ...baseFields,
179
+ { type: 'timestamptz', name: 'atstz' }, // duckdb supports timestamptz
180
+ ],
181
+ };
182
+ // BigQuery-compatible table definition (no timestamptz support)
183
+ exports.bqTableDef = {
184
+ type: 'table',
185
+ name: 'aTable',
186
+ dialect: 'standardsql',
187
+ tablePath: 'aTable',
188
+ connection: '_bq_',
189
+ fields: baseFields,
190
+ };
191
+ /**
192
+ * A TestTranlator never actually talks to connection, instead uses
193
+ * some mocked schema definitions.
194
+ */
195
+ exports.mockSchema = [
196
+ exports.aTableDef,
197
+ exports.bqTableDef,
198
+ {
178
199
  type: 'table',
179
- name: 'malloytest.carriers',
180
- dialect: 'standardsql',
200
+ name: 'carriers',
201
+ dialect: exports.TEST_DIALECT,
181
202
  tablePath: 'malloytest.carriers',
182
- connection: 'bigquery',
203
+ connection: '_db_',
183
204
  fields: [
184
205
  { name: 'code', type: 'string' },
185
206
  { name: 'name', type: 'string' },
186
207
  { name: 'nickname', type: 'string' },
187
208
  ],
188
- as: 'carriers',
189
209
  },
190
- 'malloytest.flights': {
210
+ {
191
211
  type: 'table',
192
- name: 'malloytest.flights',
193
- dialect: 'standardsql',
212
+ name: 'flights',
213
+ dialect: exports.TEST_DIALECT,
194
214
  tablePath: 'malloytest.flights',
195
- connection: 'bigquery',
215
+ connection: '_db_',
196
216
  fields: [
197
217
  { name: 'carrier', type: 'string' },
198
218
  { name: 'origin', type: 'string' },
@@ -211,14 +231,13 @@ const mockSchema = {
211
231
  { name: 'diverted', type: 'string' },
212
232
  { name: 'id2', type: 'number', numberType: 'integer' },
213
233
  ],
214
- as: 'flights',
215
234
  },
216
- 'malloytest.airports': {
235
+ {
217
236
  type: 'table',
218
- name: 'malloytest.airports',
219
- dialect: 'standardsql',
237
+ name: 'airports',
238
+ dialect: exports.TEST_DIALECT,
220
239
  tablePath: 'malloytest.airports',
221
- connection: 'bigquery',
240
+ connection: '_db_',
222
241
  fields: [
223
242
  { name: 'id', type: 'number', numberType: 'integer' },
224
243
  { name: 'code', type: 'string' },
@@ -248,17 +267,11 @@ const mockSchema = {
248
267
  { name: 'cntl_twr', type: 'string' },
249
268
  { name: 'major', type: 'string' },
250
269
  ],
251
- as: 'airports',
252
270
  },
253
- };
254
- exports.aTableDef = mockSchema['aTable'];
271
+ ];
255
272
  const bJoinedIntoA = {
256
- type: 'table',
257
- name: 'aTable',
258
- dialect: 'standardsql',
259
- tablePath: 'aTable',
260
- connection: 'test',
261
- as: 'b',
273
+ ...exports.aTableDef,
274
+ name: 'b',
262
275
  join: 'one',
263
276
  matrixOperation: 'left',
264
277
  onExpression: {
@@ -268,7 +281,6 @@ const bJoinedIntoA = {
268
281
  right: { node: 'field', path: ['b', 'astr'] },
269
282
  },
270
283
  },
271
- fields: exports.aTableDef.fields,
272
284
  };
273
285
  /**
274
286
  * When translating partial trees, there will not be a document node
@@ -323,29 +335,44 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
323
335
  this.testSrc = testSrc;
324
336
  this.allDialectsEnabled = true;
325
337
  /*
338
+ * There are two connections:
339
+ * _db_ - duckdb dialect, with the following tables ...
340
+ * aTable, malloytest.carriers, malloytest.flights, malloytest.airports
341
+ * _bq_ - bigquery/standardsql dialect, with one table
342
+ * aTable
343
+ *
344
+ * The "aTable" table is a mocked table with one column of each type.
345
+ * The _bq_ version does not have the timestamptz column, and when
346
+ * DATETIME support is added, the _db_ version will not have that.
347
+ *
326
348
  * All test source files can assume that an import of this
327
-
328
349
  *
329
- * Also the following tables will be available on _db_
330
- * aTable, malloytest.carriers, malloytest.flights, malloytest.airports
350
+ * source:
351
+ * bq_a is _bq_.table('aTable') extend { primary_key: astr }
352
+ * carriers is _db_.table('malloytest.carriers')
353
+ * flights is _db_.table('malloytest.flights')
354
+ * airports is _db_.table('malloytest.airports')
355
+ * a is _db_.table('aTable') extend { primary_key: astr }
356
+ * b is a
357
+ * ab is a extend {
358
+ * join_one: b with astr
359
+ * measure: acount is count()
360
+ * view: aturtle is { group_by: astr; aggregate: acount }
361
+ * }
331
362
  *
332
- * source: a is _db_.table('aTable') extend { primary_key: astr }
333
- * source: b is a
334
- * source: ab is a extend {
335
- * join_one: b with astr
336
- * measure: acount is count()
337
- * query: aturtle is { group_by: astr; aggregate: acount }
338
- * }
339
363
  */
340
364
  this.internalModel = {
341
365
  name: testURI,
342
366
  exports: [],
343
367
  queryList: [],
368
+ sourceRegistry: {},
344
369
  dependencies: {},
345
370
  contents: {
346
371
  _db_: { type: 'connection', name: '_db_' },
347
- a: { ...exports.aTableDef, primaryKey: 'astr', as: 'a' },
348
- b: { ...exports.aTableDef, primaryKey: 'astr', as: 'b' },
372
+ _bq_: { type: 'connection', name: '_bq_' },
373
+ a: { ...exports.aTableDef, primaryKey: 'astr', name: 'a' },
374
+ b: { ...exports.aTableDef, primaryKey: 'astr', name: 'b' },
375
+ bq_a: { ...exports.bqTableDef, primaryKey: 'astr', name: 'bq_a' },
349
376
  ab: {
350
377
  ...exports.aTableDef,
351
378
  primaryKey: 'astr',
@@ -378,7 +405,7 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
378
405
  { type: 'string', name: 'acount' },
379
406
  ],
380
407
  connection: 'test',
381
- dialect: 'standardsql',
408
+ dialect: exports.TEST_DIALECT,
382
409
  },
383
410
  isRepeated: true,
384
411
  },
@@ -393,9 +420,8 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
393
420
  if (internalModel !== undefined) {
394
421
  this.internalModel = internalModel;
395
422
  }
396
- for (const tableName in mockSchema) {
397
- this.schemaZone.define(tableName, mockSchema[tableName]);
398
- this.schemaZone.define(`_db_:${tableName}`, mockSchema[tableName]);
423
+ for (const actualSchema of exports.mockSchema) {
424
+ this.schemaZone.define(`${actualSchema.connection}:${actualSchema.tablePath}`, actualSchema);
399
425
  }
400
426
  }
401
427
  translate() {
@@ -645,7 +671,7 @@ function getSelectOneStruct(sqlBlock) {
645
671
  [key]: {
646
672
  type: 'sql_select',
647
673
  name: key,
648
- dialect: 'standardsql',
674
+ dialect: exports.TEST_DIALECT,
649
675
  connection: '_db_',
650
676
  selectStr: sqlBlock.selectStr,
651
677
  fields: [{ type: 'number', name: 'one' }],
@@ -64,6 +64,7 @@ export type HelpContextResponse = Partial<HelpContext>;
64
64
  interface TranslatedResponseData extends ResponseBase, NeededData, ProblemResponse, FinalResponse {
65
65
  modelDef: ModelDef;
66
66
  fromSources: string[];
67
+ modelWasModified: boolean;
67
68
  }
68
69
  interface TablePath extends ResponseBase, NeededData, ProblemResponse, FinalResponse {
69
70
  pathInfo: PathInfo[];
@@ -84,18 +84,17 @@ class ConstantQueryStruct extends query_node_1.QueryStruct {
84
84
  connection: dialect.name,
85
85
  dialect: dialect.name,
86
86
  };
87
- // Create a minimal model interface that only provides eventStream
88
- const minimalModel = { eventStream };
89
- // Create minimal prepare result options
90
- const minimalPrepareOptions = {};
87
+ // Create minimal model with empty structs map
88
+ const minimalModel = {
89
+ structs: new Map(),
90
+ };
91
+ // Create minimal prepare result options with eventStream
92
+ const minimalPrepareOptions = { eventStream };
91
93
  // Call parent constructor with minimal requirements
92
94
  super(minimalStructDef, undefined, // no source arguments initially
93
95
  { model: minimalModel }, minimalPrepareOptions);
94
96
  this.dialect = dialect;
95
97
  this._constantArguments = parameters;
96
- if (eventStream) {
97
- this.model.eventStream = eventStream;
98
- }
99
98
  }
100
99
  /**
101
100
  * Override arguments() to return our parameters
@@ -4,5 +4,7 @@ import { QueryQuery } from './query_query';
4
4
  import { QueryModelImpl } from './query_model_impl';
5
5
  export { QueryField, QueryStruct, QueryQuery, QueryModelImpl as QueryModel };
6
6
  export { getResultStructDefForQuery, getResultStructDefForView, } from './query_model_impl';
7
- export { indent, composeSQLExpr } from './utils';
7
+ export { indent, composeSQLExpr, makeDigest, mkModelDef } from './utils';
8
8
  export { constantExprToSQL } from './constant_expression_compiler';
9
+ export { getCompiledSQL } from './sql_compiled';
10
+ export { mkSourceID, mkBuildID, mkQuerySourceDef, mkSQLSourceDef, mkTableSourceDef, resolveSourceID, registerSource, hasSourceRegistryEntry, } from './source_def_utils';
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.constantExprToSQL = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
39
+ exports.hasSourceRegistryEntry = exports.registerSource = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.getCompiledSQL = exports.constantExprToSQL = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
40
40
  __exportStar(require("./malloy_types"), exports);
41
41
  const query_node_1 = require("./query_node");
42
42
  Object.defineProperty(exports, "QueryField", { enumerable: true, get: function () { return query_node_1.QueryField; } });
@@ -47,15 +47,8 @@ Object.defineProperty(exports, "QueryQuery", { enumerable: true, get: function (
47
47
  const field_instance_1 = require("./field_instance");
48
48
  const query_model_impl_1 = require("./query_model_impl");
49
49
  Object.defineProperty(exports, "QueryModel", { enumerable: true, get: function () { return query_model_impl_1.QueryModelImpl; } });
50
- // We have a circular dependency issue, and this is the minimal
51
- // dependency injection needed to get around it. Ideally, we would
52
- // like to avoid this, and I think a thoughtful pass through
53
- // FieldInstance and QueryField might eliminate the need for this.
54
50
  function getLookupFun(mri) {
55
- if (mri instanceof query_model_impl_1.QueryModelImpl) {
56
- return (name) => mri.structs.get(name);
57
- }
58
- return () => undefined;
51
+ return (name) => mri.structs.get(name);
59
52
  }
60
53
  field_instance_1.FieldInstanceField.registerExpressionCompiler(expression_compiler_1.exprToSQL);
61
54
  query_node_1.QueryStruct.registerTurtleFieldMaker((field, parent) => query_query_1.QueryQuery.makeQuery(field, parent, undefined, false, getLookupFun(parent.getModel())));
@@ -65,6 +58,19 @@ Object.defineProperty(exports, "getResultStructDefForView", { enumerable: true,
65
58
  var utils_1 = require("./utils");
66
59
  Object.defineProperty(exports, "indent", { enumerable: true, get: function () { return utils_1.indent; } });
67
60
  Object.defineProperty(exports, "composeSQLExpr", { enumerable: true, get: function () { return utils_1.composeSQLExpr; } });
61
+ Object.defineProperty(exports, "makeDigest", { enumerable: true, get: function () { return utils_1.makeDigest; } });
62
+ Object.defineProperty(exports, "mkModelDef", { enumerable: true, get: function () { return utils_1.mkModelDef; } });
68
63
  var constant_expression_compiler_1 = require("./constant_expression_compiler");
69
64
  Object.defineProperty(exports, "constantExprToSQL", { enumerable: true, get: function () { return constant_expression_compiler_1.constantExprToSQL; } });
65
+ var sql_compiled_1 = require("./sql_compiled");
66
+ Object.defineProperty(exports, "getCompiledSQL", { enumerable: true, get: function () { return sql_compiled_1.getCompiledSQL; } });
67
+ var source_def_utils_1 = require("./source_def_utils");
68
+ Object.defineProperty(exports, "mkSourceID", { enumerable: true, get: function () { return source_def_utils_1.mkSourceID; } });
69
+ Object.defineProperty(exports, "mkBuildID", { enumerable: true, get: function () { return source_def_utils_1.mkBuildID; } });
70
+ Object.defineProperty(exports, "mkQuerySourceDef", { enumerable: true, get: function () { return source_def_utils_1.mkQuerySourceDef; } });
71
+ Object.defineProperty(exports, "mkSQLSourceDef", { enumerable: true, get: function () { return source_def_utils_1.mkSQLSourceDef; } });
72
+ Object.defineProperty(exports, "mkTableSourceDef", { enumerable: true, get: function () { return source_def_utils_1.mkTableSourceDef; } });
73
+ Object.defineProperty(exports, "resolveSourceID", { enumerable: true, get: function () { return source_def_utils_1.resolveSourceID; } });
74
+ Object.defineProperty(exports, "registerSource", { enumerable: true, get: function () { return source_def_utils_1.registerSource; } });
75
+ Object.defineProperty(exports, "hasSourceRegistryEntry", { enumerable: true, get: function () { return source_def_utils_1.hasSourceRegistryEntry; } });
70
76
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,5 @@
1
1
  import type * as Malloy from '@malloydata/malloy-interfaces';
2
+ import type { EventStream } from '../runtime_types';
2
3
  /**
3
4
  * Field computations are compiled into an expression tree of "Expr"
4
5
  * type nodes. Each node is one of these three interfaces. The
@@ -582,7 +583,7 @@ export interface Query extends Pipeline, Filtered, HasLocation {
582
583
  modelAnnotation?: Annotation;
583
584
  compositeResolvedSourceDef?: SourceDef;
584
585
  }
585
- export type NamedQuery = Query & NamedObject;
586
+ export type NamedQueryDef = Query & NamedObject;
586
587
  export type PipeSegment = QuerySegment | IndexSegment | RawSegment;
587
588
  export declare function segmentHasErrors(segment: PipeSegment): boolean;
588
589
  export declare function structHasErrors(struct: StructDef): boolean;
@@ -685,6 +686,8 @@ interface StructDefBase extends HasLocation, NamedObject {
685
686
  annotation?: Annotation;
686
687
  modelAnnotation?: ModelAnnotation;
687
688
  fields: FieldDef[];
689
+ /** Marker for error placeholder structs created by ErrorFactory */
690
+ errorFactory?: boolean;
688
691
  }
689
692
  export interface PartitionCompositeDesc {
690
693
  partitionField: string;
@@ -716,13 +719,46 @@ export interface CompositeSourceDef extends SourceDefBase {
716
719
  export interface SQLStringSegment {
717
720
  sql: string;
718
721
  }
719
- export type SQLPhraseSegment = Query | SQLStringSegment;
722
+ export type SQLPhraseSegment = Query | PersistableSourceDef | SQLStringSegment;
720
723
  export declare function isSegmentSQL(f: SQLPhraseSegment): f is SQLStringSegment;
721
- export interface SQLSourceDef extends SourceDefBase {
724
+ export declare function isSegmentSource(f: SQLPhraseSegment): f is PersistableSourceDef;
725
+ /** Format: "name@modelUrl" - uniquely identifies a source for persistence */
726
+ export type SourceID = string;
727
+ /** Hash of (connectionDigest, sql) - uniquely identifies a built artifact */
728
+ export type BuildID = string;
729
+ /**
730
+ * Reference to a source in modelDef.contents by name.
731
+ * Used in sourceRegistry to avoid duplicating SourceDefs that are in the namespace.
732
+ */
733
+ export interface SourceRegistryReference {
734
+ type: 'source_registry_reference';
735
+ name: string;
736
+ }
737
+ /**
738
+ * Inner entry type: either a reference to contents or an actual PersistableSourceDef.
739
+ * - SourceRegistryReference: source is in namespace (contents), look it up by name
740
+ * - PersistableSourceDef: source is not in namespace (hidden dependency), stored directly
741
+ */
742
+ export type SourceRegistryEntry = SourceRegistryReference | PersistableSourceDef;
743
+ /**
744
+ * Value in the sourceRegistry, wrapping the entry with persistence info.
745
+ * persist is lazily computed: undefined = not checked, true/false = checked
746
+ */
747
+ export interface SourceRegistryValue {
748
+ entry: SourceRegistryEntry;
749
+ persist?: boolean;
750
+ }
751
+ export declare function isSourceRegistryReference(entry: SourceRegistryEntry): entry is SourceRegistryReference;
752
+ export interface PersistableSourceProperties {
753
+ sourceID?: SourceID;
754
+ extends?: SourceID;
755
+ }
756
+ export interface SQLSourceDef extends SourceDefBase, PersistableSourceProperties {
722
757
  type: 'sql_select';
723
758
  selectStr: string;
759
+ selectSegments?: SQLPhraseSegment[];
724
760
  }
725
- export interface QuerySourceDef extends SourceDefBase {
761
+ export interface QuerySourceDef extends SourceDefBase, PersistableSourceProperties {
726
762
  type: 'query_source';
727
763
  query: Query;
728
764
  }
@@ -738,7 +774,21 @@ export interface FinalizeSourceDef extends SourceDefBase {
738
774
  }
739
775
  export declare function sourceBase(sd: SourceDefBase): SourceDefBase;
740
776
  export declare function isSourceDef(sd: NamedModelObject | FieldDef): sd is SourceDef;
777
+ /**
778
+ * Union of all source definition types.
779
+ *
780
+ * IMPORTANT: Never use object spread to copy a SourceDef. Use the factory
781
+ * methods in source_def_utils.ts to merge changes into a source def:
782
+ * - mkSQLSourceDef(base, ...) - create SQLSourceDef from base
783
+ * - mkQuerySourceDef(base, ...) - create QuerySourceDef from base
784
+ *
785
+ * These factories explicitly copy only safe fields, preventing accidental
786
+ * propagation of sourceID/extends which must only be set in DefineSource.
787
+ */
741
788
  export type SourceDef = TableSourceDef | SQLSourceDef | QuerySourceDef | QueryResultDef | FinalizeSourceDef | NestSourceDef | CompositeSourceDef;
789
+ /** Sources that can be persisted (materialized to tables) */
790
+ export type PersistableSourceDef = SQLSourceDef | QuerySourceDef;
791
+ export declare function isPersistableSourceDef(sd: SourceDef): sd is PersistableSourceDef;
742
792
  /** Is this the "FROM" table of a query tree */
743
793
  export declare function isBaseTable(def: StructDef): def is SourceDef;
744
794
  export type StructDef = SourceDef | RecordDef | ArrayDef;
@@ -891,7 +941,7 @@ export type QueryFieldDef = AtomicFieldDef | TurtleDef | RefToField;
891
941
  export type TypedDef = AtomicTypeDef | JoinFieldDef | TurtleDef | RefToField | StructDef;
892
942
  /** Get the output name for a NamedObject */
893
943
  export declare function getIdentifier(n: AliasedName): string;
894
- export type NamedModelObject = SourceDef | NamedQuery | FunctionDef | ConnectionDef;
944
+ export type NamedModelObject = SourceDef | NamedQueryDef | FunctionDef | ConnectionDef;
895
945
  export interface DependencyTree {
896
946
  [url: string]: DependencyTree;
897
947
  }
@@ -900,6 +950,13 @@ export interface ModelDef {
900
950
  name: string;
901
951
  exports: string[];
902
952
  contents: Record<string, NamedModelObject>;
953
+ /**
954
+ * Registry mapping sourceID to source definitions for build graph construction.
955
+ * For sources in namespace: maps to SourceRegistryReference (look up in contents)
956
+ * For hidden dependencies: maps to actual PersistableSourceDef (not in namespace)
957
+ * Each entry includes a lazily-computed persist flag.
958
+ */
959
+ sourceRegistry: Record<SourceID, SourceRegistryValue>;
903
960
  annotation?: ModelAnnotation;
904
961
  queryList: Query[];
905
962
  dependencies: DependencyTree;
@@ -948,12 +1005,6 @@ export interface DrillSource {
948
1005
  sourceFilters?: FilterCondition[];
949
1006
  sourceArguments?: Record<string, Argument>;
950
1007
  }
951
- export type QueryToMaterialize = {
952
- id: string;
953
- path: string;
954
- source: string | undefined;
955
- queryName: string;
956
- };
957
1008
  export interface CompiledQuery extends DrillSource {
958
1009
  structs: SourceDef[];
959
1010
  sql: string;
@@ -963,8 +1014,6 @@ export interface CompiledQuery extends DrillSource {
963
1014
  connectionName: string;
964
1015
  queryTimezone?: string;
965
1016
  annotation?: Annotation;
966
- dependenciesToMaterialize?: Record<string, QueryToMaterialize>;
967
- materialization?: QueryToMaterialize;
968
1017
  defaultRowLimitAdded?: number;
969
1018
  }
970
1019
  /** Result type for running a Malloy query. */
@@ -1007,10 +1056,15 @@ export interface SearchValueMapResult {
1007
1056
  }[];
1008
1057
  }
1009
1058
  export interface PrepareResultOptions {
1010
- replaceMaterializedReferences?: boolean;
1011
- materializedTablePrefix?: string;
1012
1059
  defaultRowLimit?: number;
1013
1060
  isPartialQuery?: boolean;
1061
+ eventStream?: EventStream;
1062
+ /** Manifest of built tables (BuildID → entry), the build cache */
1063
+ buildManifest?: BuildManifest;
1064
+ /** Map from connectionName to connectionDigest (from Connection.getDigest()) */
1065
+ connectionDigests?: Record<string, string>;
1066
+ /** If true, throw when a persist query's digest is not in the manifest */
1067
+ strictPersist?: boolean;
1014
1068
  }
1015
1069
  type UTD = AtomicTypeDef | TypedDef | FunctionParameterTypeDef | FunctionReturnTypeDef | undefined;
1016
1070
  /**
@@ -1048,4 +1102,24 @@ export type UniqueKeyRequirement = undefined | {
1048
1102
  isCount: boolean;
1049
1103
  };
1050
1104
  export declare function mergeUniqueKeyRequirement(existing: UniqueKeyRequirement, newInfo: UniqueKeyRequirement): UniqueKeyRequirement;
1105
+ /**
1106
+ * Entry in a BuildManifest for a persisted table.
1107
+ */
1108
+ export interface BuildManifestEntry {
1109
+ /** Hash of (connectionDigest, sql) - also the key in buildEntries */
1110
+ buildId: BuildID;
1111
+ tableName: string;
1112
+ buildStartedAt: string;
1113
+ buildFinishedAt: string;
1114
+ }
1115
+ /**
1116
+ * Manifest of persisted query results (the build cache).
1117
+ * Used by compileQuery to substitute persist queries with table references.
1118
+ */
1119
+ export interface BuildManifest {
1120
+ modelUrl: string;
1121
+ buildStartedAt: string;
1122
+ buildFinishedAt: string;
1123
+ buildEntries: Record<BuildID, BuildManifestEntry>;
1124
+ }
1051
1125
  export {};
@@ -76,8 +76,11 @@ exports.isRawSegment = isRawSegment;
76
76
  exports.isIndexSegment = isIndexSegment;
77
77
  exports.bareFieldUsage = bareFieldUsage;
78
78
  exports.isSegmentSQL = isSegmentSQL;
79
+ exports.isSegmentSource = isSegmentSource;
80
+ exports.isSourceRegistryReference = isSourceRegistryReference;
79
81
  exports.sourceBase = sourceBase;
80
82
  exports.isSourceDef = isSourceDef;
83
+ exports.isPersistableSourceDef = isPersistableSourceDef;
81
84
  exports.isBaseTable = isBaseTable;
82
85
  exports.isLiteral = isLiteral;
83
86
  exports.mergeEvalSpaces = mergeEvalSpaces;
@@ -443,6 +446,12 @@ function bareFieldUsage(fu) {
443
446
  function isSegmentSQL(f) {
444
447
  return 'sql' in f;
445
448
  }
449
+ function isSegmentSource(f) {
450
+ return 'type' in f && (f.type === 'sql_select' || f.type === 'query_source');
451
+ }
452
+ function isSourceRegistryReference(entry) {
453
+ return entry.type === 'source_registry_reference';
454
+ }
446
455
  // The gesture {...sourceStruct moreProperties} happens everywhere, now that
447
456
  // structs aren't all identical, we need a way to make one from any of the
448
457
  // exisitng structs
@@ -458,6 +467,9 @@ function isSourceDef(sd) {
458
467
  sd.type === 'nest_source' ||
459
468
  sd.type === 'composite');
460
469
  }
470
+ function isPersistableSourceDef(sd) {
471
+ return sd.type === 'sql_select' || sd.type === 'query_source';
472
+ }
461
473
  /** Is this the "FROM" table of a query tree */
462
474
  function isBaseTable(def) {
463
475
  if (isJoinedSource(def)) {
@@ -0,0 +1,47 @@
1
+ import type { ModelDef, SourceDef, Query } from './malloy_types';
2
+ import type { BuildNode } from '../api/foundation/types';
3
+ /**
4
+ * Find persistent dependencies for a source or query, returning a nested DAG.
5
+ *
6
+ * Walks the full dependency tree but only includes persistent sources in the
7
+ * result. Non-persistent sources are "flattened out" - their persistent
8
+ * dependencies bubble up to become direct dependencies of the caller.
9
+ *
10
+ * Example: source_c (persist) -> source_b (NOT persist) -> source_a (persist)
11
+ * Returns: [{sourceID: source_a, dependsOn: []}]
12
+ * (source_b is flattened out, source_a becomes direct dependency)
13
+ *
14
+ * ## The 6 Dependency Paths in the IR
15
+ *
16
+ * Starting from a Query or SourceDef, these are ALL the ways a SourceDef
17
+ * can be referenced (and thus must be walked for dependency tracking):
18
+ *
19
+ * 1. **Query.structRef** → SourceDef (the FROM clause)
20
+ * 2. **Query.pipeline[].extendSource[]** → JoinFieldDef (joins in extend blocks)
21
+ * 3. **SourceDef.fields[]** → JoinFieldDef (joins defined on a source)
22
+ * 4. **PersistableSourceDef.extends** → SourceID (extend chain reference)
23
+ * 5. **SQLSourceDef.selectSegments[]** → Query | PersistableSourceDef (SQL interpolation)
24
+ * 6. **QuerySourceDef.query** → Query (nested query in query_source)
25
+ *
26
+ * Note: CompositeSourceDef.sources[] is ignored - composite sources and
27
+ * persistence may be incompatible features.
28
+ *
29
+ * @param root The source or query to find dependencies for
30
+ * @param modelDef The model definition containing the source registry
31
+ * @returns Array of BuildNode representing the persistent dependency DAG
32
+ */
33
+ export declare function findPersistentDependencies(root: SourceDef | Query, modelDef: ModelDef): BuildNode[];
34
+ /**
35
+ * Find the minimal set of root build graphs from a forest of BuildNodes.
36
+ *
37
+ * Uses flattening for ANALYSIS ONLY to identify unique nodes and find roots.
38
+ * Returns original graph structures (NOT flattened) - preserves branching
39
+ * for parallel builds.
40
+ *
41
+ * Roots are sourceIDs that exist but nothing depends on them - these are
42
+ * the entry points for building.
43
+ *
44
+ * @param deps Array of BuildNode trees (potentially overlapping)
45
+ * @returns Array of root BuildNode trees (deduplicated)
46
+ */
47
+ export declare function minimalBuildGraph(deps: BuildNode[]): BuildNode[];