@malloydata/malloy 0.0.435 → 0.0.436

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 (76) hide show
  1. package/dist/api/foundation/build_targets.d.ts +5 -6
  2. package/dist/api/foundation/build_targets.js +74 -6
  3. package/dist/api/foundation/core.d.ts +16 -4
  4. package/dist/api/foundation/core.js +23 -23
  5. package/dist/api/foundation/index.d.ts +1 -1
  6. package/dist/api/foundation/runtime.d.ts +21 -2
  7. package/dist/api/foundation/runtime.js +44 -31
  8. package/dist/api/foundation/types.d.ts +43 -3
  9. package/dist/connection/dbmeta.d.ts +29 -0
  10. package/dist/connection/dbmeta.js +46 -0
  11. package/dist/connection/index.d.ts +1 -0
  12. package/dist/connection/index.js +1 -0
  13. package/dist/dialect/databricks/databricks.d.ts +11 -12
  14. package/dist/dialect/databricks/databricks.js +24 -36
  15. package/dist/dialect/dialect.d.ts +56 -170
  16. package/dist/dialect/dialect.js +89 -90
  17. package/dist/dialect/duckdb/duckdb.d.ts +2 -2
  18. package/dist/dialect/duckdb/duckdb.js +1 -1
  19. package/dist/dialect/index.d.ts +1 -1
  20. package/dist/dialect/index.js +2 -2
  21. package/dist/dialect/mysql/mysql.d.ts +11 -12
  22. package/dist/dialect/mysql/mysql.js +20 -32
  23. package/dist/dialect/pg_impl.d.ts +9 -10
  24. package/dist/dialect/pg_impl.js +27 -73
  25. package/dist/dialect/postgres/postgres.d.ts +4 -4
  26. package/dist/dialect/postgres/postgres.js +4 -12
  27. package/dist/dialect/snowflake/snowflake.d.ts +11 -13
  28. package/dist/dialect/snowflake/snowflake.js +30 -82
  29. package/dist/dialect/sqlserver/sqlserver.d.ts +12 -13
  30. package/dist/dialect/sqlserver/sqlserver.js +46 -47
  31. package/dist/dialect/standardsql/standardsql.d.ts +11 -16
  32. package/dist/dialect/standardsql/standardsql.js +22 -73
  33. package/dist/dialect/trino/trino.d.ts +16 -18
  34. package/dist/dialect/trino/trino.js +127 -181
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +2 -2
  37. package/dist/lang/ast/expressions/boolean-value.d.ts +24 -0
  38. package/dist/lang/ast/expressions/boolean-value.js +41 -0
  39. package/dist/lang/ast/expressions/case.js +7 -0
  40. package/dist/lang/ast/expressions/expr-alternation-tree.js +11 -0
  41. package/dist/lang/ast/expressions/expr-cast.js +5 -0
  42. package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
  43. package/dist/lang/ast/expressions/expr-func.js +13 -2
  44. package/dist/lang/ast/expressions/expr-minus.js +5 -2
  45. package/dist/lang/ast/expressions/expr-null.js +4 -0
  46. package/dist/lang/ast/expressions/pick-when.js +7 -0
  47. package/dist/lang/ast/field-space/index-field-space.js +13 -1
  48. package/dist/lang/ast/field-space/query-spaces.js +11 -0
  49. package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
  50. package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
  51. package/dist/lang/ast/query-items/field-declaration.js +22 -16
  52. package/dist/lang/ast/query-items/field-references.d.ts +6 -0
  53. package/dist/lang/ast/query-items/field-references.js +23 -0
  54. package/dist/lang/ast/query-properties/filters.js +3 -1
  55. package/dist/lang/ast/statements/import-statement.js +13 -0
  56. package/dist/lang/ast/types/expression-def.js +17 -2
  57. package/dist/lang/ast/types/malloy-element.d.ts +24 -1
  58. package/dist/lang/ast/types/malloy-element.js +63 -26
  59. package/dist/lang/ast/view-elements/reference-view.js +4 -0
  60. package/dist/lang/ast/view-elements/refine-utils.js +1 -0
  61. package/dist/lang/composite-source-utils.js +10 -17
  62. package/dist/lang/parse-log.d.ts +12 -0
  63. package/dist/lang/parse-log.js +2 -0
  64. package/dist/lang/test/test-translator.d.ts +2 -1
  65. package/dist/lang/test/test-translator.js +37 -3
  66. package/dist/model/expression_compiler.js +8 -7
  67. package/dist/model/filter_compilers.js +13 -12
  68. package/dist/model/given_binding.d.ts +8 -0
  69. package/dist/model/given_binding.js +13 -0
  70. package/dist/model/index.d.ts +1 -1
  71. package/dist/model/index.js +2 -1
  72. package/dist/model/sql_compiled.d.ts +19 -20
  73. package/dist/model/sql_compiled.js +29 -17
  74. package/dist/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/package.json +4 -4
@@ -2,7 +2,7 @@ import type { BuildIdOptions } from '../../model';
2
2
  import type { LogMessage } from '../../lang';
3
3
  import type { PersistNode } from '../../model/persist_utils';
4
4
  import type { Model, PersistSource } from './core';
5
- import type { ConnectionBuild } from './types';
5
+ import type { BuildTargets } from './types';
6
6
  /**
7
7
  * A walk node with its source already resolved.
8
8
  *
@@ -47,9 +47,8 @@ export declare function resolvePersistWalk(model: Model, tagParseLog: LogMessage
47
47
  *
48
48
  * @param walk A resolved walk from {@link resolvePersistWalk}, in order
49
49
  * @param connectionDigests One digest per connection named by a persist source
50
- * @param buildIdOpts What the BuildID's SQL is compiled under. The compiler
51
- * recomputes the same key at serve time through `buildIdOptions()`; supply a
52
- * different `virtualMap` here and the table is built under a key nothing
53
- * looks up.
50
+ * @param buildIdOpts What every BuildID's SQL is generated under.
51
+ * `BuildTarget.buildSQL` generates its SQL under the same options, and
52
+ * `persistedTableFor` recomputes the key under a query's.
54
53
  */
55
- export declare function mkBuildTargets(walk: ResolvedNode[], connectionDigests: Record<string, string>, buildIdOpts: BuildIdOptions): ConnectionBuild[];
54
+ export declare function mkBuildTargets(walk: ResolvedNode[], connectionDigests: Record<string, string>, buildIdOpts: BuildIdOptions): Pick<BuildTargets, 'connections' | 'unbuildable'>;
@@ -6,6 +6,8 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.resolvePersistWalk = resolvePersistWalk;
8
8
  exports.mkBuildTargets = mkBuildTargets;
9
+ const model_1 = require("../../model");
10
+ const given_binding_1 = require("../../model/given_binding");
9
11
  /**
10
12
  * Read a key this function's own construction guarantees is present.
11
13
  *
@@ -38,6 +40,54 @@ function resolvePersistWalk(model, tagParseLog) {
38
40
  }
39
41
  return resolved;
40
42
  }
43
+ /**
44
+ * Describes a given that `source` reads and this build has no value for.
45
+ * Names it by the name this model surfaces it under, since that is the name a
46
+ * builder supplies a value by: under `import { T is TENANT }` it is `T`.
47
+ */
48
+ function unvaluedGivenProblem(source, use) {
49
+ var _a, _b, _c, _d, _e;
50
+ const modelDef = source._model._modelDef;
51
+ const surfaced = Object.entries(modelDef.contents).find(([, entry]) => entry.type === 'given' && entry.id === use.id);
52
+ const name = (_d = (_a = surfaced === null || surfaced === void 0 ? void 0 : surfaced[0]) !== null && _a !== void 0 ? _a : (_c = (_b = modelDef.givens) === null || _b === void 0 ? void 0 : _b[use.id]) === null || _c === void 0 ? void 0 : _c.name) !== null && _d !== void 0 ? _d : use.id;
53
+ return {
54
+ severity: 'error',
55
+ code: 'runtime-persist-given-no-value',
56
+ at: (_e = use.at) !== null && _e !== void 0 ? _e : source.location,
57
+ message: `Persisted source '${source.sourceID}' reads given '${name}', which ` +
58
+ `has no value in this build and no default. Supply '${name}' to the ` +
59
+ 'build, or give it a default at its declaration.',
60
+ };
61
+ }
62
+ /**
63
+ * Generates the SQL that keys `source`'s table, or returns why no key can be
64
+ * computed. A `MalloyCompileError` is a problem with the source; any other
65
+ * error is the compiler's own and propagates.
66
+ */
67
+ function buildIdSQL(source, opts) {
68
+ var _a;
69
+ const reads = (0, model_1.givenUsageOfRelation)(source._sourceDef);
70
+ const givens = source._model._modelDef.givens;
71
+ const unvalued = reads.find(use => (0, given_binding_1.lookupGivenValue)(opts.resolvedGivens, givens, use.id) === undefined);
72
+ if (unvalued !== undefined) {
73
+ return { problem: unvaluedGivenProblem(source, unvalued) };
74
+ }
75
+ try {
76
+ return { sql: source._compileSQL(opts), readsGivens: reads.length > 0 };
77
+ }
78
+ catch (e) {
79
+ if (!(e instanceof model_1.MalloyCompileError))
80
+ throw e;
81
+ return {
82
+ problem: {
83
+ message: e.message,
84
+ severity: 'error',
85
+ code: e.code,
86
+ at: (_a = e.at) !== null && _a !== void 0 ? _a : source.location,
87
+ },
88
+ };
89
+ }
90
+ }
41
91
  /** A BuildID is a hex digest, so it cannot contain the separator. */
42
92
  function targetKey(connectionName, buildId) {
43
93
  return `${connectionName}:${buildId}`;
@@ -75,14 +125,14 @@ function addUnique(into, seen, keys) {
75
125
  *
76
126
  * @param walk A resolved walk from {@link resolvePersistWalk}, in order
77
127
  * @param connectionDigests One digest per connection named by a persist source
78
- * @param buildIdOpts What the BuildID's SQL is compiled under. The compiler
79
- * recomputes the same key at serve time through `buildIdOptions()`; supply a
80
- * different `virtualMap` here and the table is built under a key nothing
81
- * looks up.
128
+ * @param buildIdOpts What every BuildID's SQL is generated under.
129
+ * `BuildTarget.buildSQL` generates its SQL under the same options, and
130
+ * `persistedTableFor` recomputes the key under a query's.
82
131
  */
83
132
  function mkBuildTargets(walk, connectionDigests, buildIdOpts) {
84
133
  var _a;
85
134
  const targets = new Map();
135
+ const unbuildable = [];
86
136
  // What each source hands to whoever referenced it: its own target if it is
87
137
  // one, otherwise whatever it was a route to.
88
138
  const contributes = new Map();
@@ -105,7 +155,15 @@ function mkBuildTargets(walk, connectionDigests, buildIdOpts) {
105
155
  `BuildID of '${node.sourceID}'. Supply a digest for every connection ` +
106
156
  'named by a persist source.');
107
157
  }
108
- const sql = source.getSQL(buildIdOpts);
158
+ const keyed = buildIdSQL(source, buildIdOpts);
159
+ if ('problem' in keyed) {
160
+ // No key, so no table: pass its dependencies up as a route does.
161
+ // Whatever reads it inlines its SQL and ends up here too.
162
+ unbuildable.push({ source, problem: keyed.problem });
163
+ contributes.set(node.sourceID, childKeys);
164
+ continue;
165
+ }
166
+ const { sql, readsGivens } = keyed;
109
167
  const buildId = source.makeBuildId(digest, sql);
110
168
  const key = targetKey(connectionName, buildId);
111
169
  let target = targets.get(key);
@@ -114,6 +172,7 @@ function mkBuildTargets(walk, connectionDigests, buildIdOpts) {
114
172
  buildId,
115
173
  connectionName,
116
174
  sql,
175
+ readsGivens,
117
176
  sources: [],
118
177
  dependsOn: new Set(),
119
178
  };
@@ -164,10 +223,19 @@ function mkBuildTargets(walk, connectionDigests, buildIdOpts) {
164
223
  // point both ways round a diamond.
165
224
  const built = new Map();
166
225
  for (const [key, partial] of targets) {
226
+ // Every source on a target compiles to the same SQL, so any of them can
227
+ // produce the build SQL; the manifest is the only thing the builder adds.
228
+ const [source] = partial.sources;
167
229
  built.set(key, {
168
230
  buildId: partial.buildId,
169
231
  connectionName: partial.connectionName,
170
232
  sql: partial.sql,
233
+ buildSQL: (manifest) => source._compileSQL({
234
+ ...buildIdOpts,
235
+ buildManifest: manifest,
236
+ connectionDigests,
237
+ }),
238
+ readsGivens: partial.readsGivens,
171
239
  sources: partial.sources,
172
240
  dependsOn: [],
173
241
  });
@@ -220,6 +288,6 @@ function mkBuildTargets(walk, connectionDigests, buildIdOpts) {
220
288
  for (const [connectionName, connectionTargets] of byConnection) {
221
289
  connections.push({ connectionName, targets: connectionTargets });
222
290
  }
223
- return connections;
291
+ return { connections, unbuildable };
224
292
  }
225
293
  //# sourceMappingURL=build_targets.js.map
@@ -1,5 +1,5 @@
1
1
  import type { LogMessage } from '../../lang';
2
- import type { BuildID, CompiledQuery, ConstantExpr, DocumentLocation, BooleanFieldDef, JSONFieldDef, NumberFieldDef, StringFieldDef, FilterCondition, Given as InternalGiven, GivenID, GivenTypeDef, Query as InternalQuery, Pipeline, ModelDef, DocumentPosition as ModelDocumentPosition, NamedQueryDef, StructDef, TurtleDef, NativeUnsupportedFieldDef, ImportLocation, AnnotationsDef, ModelAnnotationEntry, ModelID, NamedModelObject, AtomicFieldDef, DateFieldDef, ATimestampFieldDef, SourceDef, Argument, SourceComponentInfo, DocumentReference, PersistableSourceDef } from '../../model';
2
+ import type { BuildID, CompiledQuery, ConstantExpr, DocumentLocation, BooleanFieldDef, JSONFieldDef, NumberFieldDef, StringFieldDef, FilterCondition, Given as InternalGiven, GivenID, GivenTypeDef, Query as InternalQuery, Pipeline, ModelDef, DocumentPosition as ModelDocumentPosition, NamedQueryDef, StructDef, TurtleDef, NativeUnsupportedFieldDef, ImportLocation, AnnotationsDef, ModelAnnotationEntry, ModelID, NamedModelObject, AtomicFieldDef, DateFieldDef, ATimestampFieldDef, SourceDef, Argument, SourceComponentInfo, DocumentReference, PersistableSourceDef, PrepareResultOptions } from '../../model';
3
3
  import { QueryModel } from '../../model';
4
4
  import type { Dialect } from '../../dialect';
5
5
  import type { BuildGraph, CompileQueryOptions } from './types';
@@ -603,6 +603,8 @@ export declare class PersistSource implements Taggable {
603
603
  * @param connectionDigest - Digest from connection.getDigest()
604
604
  * @param sql - The SQL for this source (from getSQL())
605
605
  * @return The BuildID for manifest lookup
606
+ * @deprecated `BuildTarget.buildId` is already computed, under the build's
607
+ * own givens and `virtualMap`. Used only with `Model.getBuildPlan`.
606
608
  */
607
609
  makeBuildId(connectionDigest: string, sql: string): BuildID;
608
610
  /**
@@ -613,12 +615,22 @@ export declare class PersistSource implements Taggable {
613
615
  *
614
616
  * @param options - Compile options. `buildManifest` and `connectionDigests`
615
617
  * substitute already-built dependencies, giving the SQL to execute.
616
- * Anything that changes the SQL without them — `virtualMap` — must match
617
- * what the compiler will use, or the table is built under a key nothing
618
- * looks up.
619
618
  * @return The SQL string for this source.
619
+ * @deprecated Read `BuildTarget.sql` and call `BuildTarget.buildSQL()`. This
620
+ * generates SQL under whatever `options` say, so it can disagree with the key
621
+ * the build computed; the target's methods cannot. Used only with
622
+ * `Model.getBuildPlan`.
620
623
  */
621
624
  getSQL(options?: CompileQueryOptions): string;
625
+ /**
626
+ * This source's SQL under `opts`: the bare `SELECT`, not finalized, because
627
+ * `persistedTableFor` hashes the same unfinalized SELECT to look the table
628
+ * up, and a dialect with a final stage (Postgres) would hash differently.
629
+ *
630
+ * @internal `opts` is whatever the caller passes; `BuildTarget.sql` and
631
+ * `buildSQL()` pass the build's own.
632
+ */
633
+ _compileSQL(opts: PrepareResultOptions): string;
622
634
  /**
623
635
  * Get the underlying persistable source definition.
624
636
  */
@@ -1347,6 +1347,8 @@ class PersistSource {
1347
1347
  * @param connectionDigest - Digest from connection.getDigest()
1348
1348
  * @param sql - The SQL for this source (from getSQL())
1349
1349
  * @return The BuildID for manifest lookup
1350
+ * @deprecated `BuildTarget.buildId` is already computed, under the build's
1351
+ * own givens and `virtualMap`. Used only with `Model.getBuildPlan`.
1350
1352
  */
1351
1353
  makeBuildId(connectionDigest, sql) {
1352
1354
  return (0, source_def_utils_1.mkBuildID)(connectionDigest, sql);
@@ -1359,26 +1361,33 @@ class PersistSource {
1359
1361
  *
1360
1362
  * @param options - Compile options. `buildManifest` and `connectionDigests`
1361
1363
  * substitute already-built dependencies, giving the SQL to execute.
1362
- * Anything that changes the SQL without them — `virtualMap` — must match
1363
- * what the compiler will use, or the table is built under a key nothing
1364
- * looks up.
1365
1364
  * @return The SQL string for this source.
1365
+ * @deprecated Read `BuildTarget.sql` and call `BuildTarget.buildSQL()`. This
1366
+ * generates SQL under whatever `options` say, so it can disagree with the key
1367
+ * the build computed; the target's methods cannot. Used only with
1368
+ * `Model.getBuildPlan`.
1366
1369
  */
1367
1370
  getSQL(options) {
1371
+ return this._compileSQL({
1372
+ ...options,
1373
+ resolvedGivens: (0, given_binding_1.bindGivens)(options === null || options === void 0 ? void 0 : options.givens, this.model._modelDef),
1374
+ });
1375
+ }
1376
+ /**
1377
+ * This source's SQL under `opts`: the bare `SELECT`, not finalized, because
1378
+ * `persistedTableFor` hashes the same unfinalized SELECT to look the table
1379
+ * up, and a dialect with a final stage (Postgres) would hash differently.
1380
+ *
1381
+ * @internal `opts` is whatever the caller passes; `BuildTarget.sql` and
1382
+ * `buildSQL()` pass the build's own.
1383
+ */
1384
+ _compileSQL(opts) {
1368
1385
  const sd = this.persistableDef;
1369
1386
  const queryModel = this.model.queryModel;
1370
- // Compile with finalize=false so this SQL is the bare source SELECT.
1371
- // The build-time key (makeBuildId over this SQL) must equal the serve-time
1372
- // manifest lookup key, which persistedTableFor recomputes from the same
1373
- // unfinalized SELECT; finalizing would diverge the two on dialects with a
1374
- // final stage (Postgres) and mis-materialize the table.
1375
1387
  if (sd.type === 'sql_select') {
1376
- return (0, model_1.getCompiledSQL)(sd, options !== null && options !== void 0 ? options : {}, (query, opts) => queryModel.compileQuery(query, opts, false).sql);
1377
- }
1378
- else {
1379
- const compiled = queryModel.compileQuery(sd.query, options, false);
1380
- return compiled.sql;
1388
+ return (0, model_1.getCompiledSQL)(sd, opts, (query, o) => queryModel.compileQuery(query, o, false).sql);
1381
1389
  }
1390
+ return queryModel.compileQuery(sd.query, opts, false).sql;
1382
1391
  }
1383
1392
  /**
1384
1393
  * Get the underlying persistable source definition.
@@ -1503,18 +1512,9 @@ class PreparedQuery extends PipelineBase {
1503
1512
  */
1504
1513
  getPreparedResult(options) {
1505
1514
  const queryModel = this._model.queryModel;
1506
- // Build the resolved-givens map in two phases:
1507
- // 1. caller-supplied values (resolveSuppliedGivens)
1508
- // 2. inline-given defaults eager-evaluated against the map
1509
- // Result is undefined when no values land — preserves the previous
1510
- // "no givens path" downstream.
1511
- const resolved = (options === null || options === void 0 ? void 0 : options.givens)
1512
- ? (0, given_binding_1.resolveSuppliedGivens)(options.givens, this._modelDef)
1513
- : new Map();
1514
- (0, given_binding_1.evaluateInlineGivens)(resolved, this._modelDef);
1515
1515
  const prepareResultOptions = {
1516
1516
  ...options,
1517
- resolvedGivens: resolved.size > 0 ? resolved : undefined,
1517
+ resolvedGivens: (0, given_binding_1.bindGivens)(options === null || options === void 0 ? void 0 : options.givens, this._modelDef),
1518
1518
  };
1519
1519
  const translatedQuery = queryModel.compileQuery(this._query, prepareResultOptions);
1520
1520
  return new PreparedResult({
@@ -1,4 +1,4 @@
1
- export type { Taggable, Loggable, ParseOptions, CompileOptions, CompileQueryOptions, BuildNode, BuildGraph, BuildTarget, BuildTargets, ConnectionBuild, } from './types';
1
+ export type { Taggable, Loggable, ParseOptions, CompileOptions, CompileQueryOptions, BuildNode, BuildGraph, BuildTarget, BuildTargetOptions, BuildTargets, ConnectionBuild, UnbuildableSource, } from './types';
2
2
  export { EMPTY_BUILD_MANIFEST } from './types';
3
3
  export { EmptyURLReader, InMemoryURLReader, FixedConnectionMap, hashForInvalidationKey, isInternalURL, readURL, getInvalidationKey, } from './readers';
4
4
  export type { ModelCache, CachedModel } from './cache';
@@ -6,7 +6,7 @@ import type { Dialect } from '../../dialect';
6
6
  import type { RunSQLOptions } from '../../run_sql_options';
7
7
  import type { CacheManager } from './cache';
8
8
  import type { MalloyConfig } from './config';
9
- import type { ParseOptions, CompileOptions, CompileQueryOptions, BuildTargets } from './types';
9
+ import type { ParseOptions, CompileOptions, CompileQueryOptions, BuildTargets, BuildTargetOptions } from './types';
10
10
  import type { PreparedResult, Explore } from './core';
11
11
  import { Model, PreparedQuery } from './core';
12
12
  import type { DataRecord, Result } from './result';
@@ -102,6 +102,18 @@ export declare class Runtime {
102
102
  * the cached promise.
103
103
  */
104
104
  getGivens(): Promise<ReadonlyMap<string, GivenValue>>;
105
+ /**
106
+ * The given values one compile runs under: the config file's, then the
107
+ * constructor's, then `perCall`, each winning per key over the one before.
108
+ * Undefined when no layer supplies anything.
109
+ *
110
+ * A per-call value for a finalized given is refused before anything else
111
+ * happens: finalizing means the Runtime decides, and that holds for a build
112
+ * as it does for a query.
113
+ *
114
+ * @internal Accessed from QueryMaterializer and getBuildTargets.
115
+ */
116
+ _givensFor(perCall: Record<string, GivenValue> | undefined): Promise<Record<string, GivenValue> | undefined>;
105
117
  /**
106
118
  * Surface names of givens locked at the runtime layer (the resolved set
107
119
  * the `config.finalizeGivens` directive produced). Per-query supply for
@@ -292,10 +304,17 @@ export declare class Runtime {
292
304
  * in dependency order and each carries its own `dependsOn`. See
293
305
  * {@link BuildTargets}.
294
306
  *
307
+ * The build generates SQL under this Runtime's givens and `virtualMap`, with
308
+ * `options` layered over them, exactly as a query would; a persisted table
309
+ * is therefore found by the queries that run under the same values. A
310
+ * source whose SQL needs a value this build does not have is reported in
311
+ * `unbuildable`, not thrown.
312
+ *
295
313
  * @param model A compiled model with `##! experimental.persistence`
314
+ * @param options Given values and a `virtualMap` for this build
296
315
  * @return The targets to build, with any annotation parse messages
297
316
  */
298
- getBuildTargets(model: Model): Promise<BuildTargets>;
317
+ getBuildTargets(model: Model, options?: BuildTargetOptions): Promise<BuildTargets>;
299
318
  }
300
319
  export declare class ConnectionRuntime extends Runtime {
301
320
  readonly rawConnections: Connection[];
@@ -6,6 +6,7 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.ExploreMaterializer = exports.PreparedResultMaterializer = exports.QueryMaterializer = exports.ModelMaterializer = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.Runtime = void 0;
8
8
  const model_1 = require("../../model");
9
+ const given_binding_1 = require("../../model/given_binding");
9
10
  const dialect_1 = require("../../dialect");
10
11
  const validate_table_path_1 = require("../../connection/validate_table_path");
11
12
  const config_1 = require("./config");
@@ -141,6 +142,33 @@ class Runtime {
141
142
  merged.set(k, v);
142
143
  return merged;
143
144
  }
145
+ /**
146
+ * The given values one compile runs under: the config file's, then the
147
+ * constructor's, then `perCall`, each winning per key over the one before.
148
+ * Undefined when no layer supplies anything.
149
+ *
150
+ * A per-call value for a finalized given is refused before anything else
151
+ * happens: finalizing means the Runtime decides, and that holds for a build
152
+ * as it does for a query.
153
+ *
154
+ * @internal Accessed from QueryMaterializer and getBuildTargets.
155
+ */
156
+ async _givensFor(perCall) {
157
+ if (this._finalizedGivensSet.size > 0 && perCall) {
158
+ for (const name of Object.keys(perCall)) {
159
+ if (this._finalizedGivensSet.has(name)) {
160
+ throw new model_1.MalloyCompileError(`Cannot supply given '${name}' per call: it is finalized at ` +
161
+ 'the runtime layer via `config.finalizeGivens`.', 'runtime-given-finalized', undefined);
162
+ }
163
+ }
164
+ }
165
+ const fileGivens = await this._resolveGivens();
166
+ const constructorGivens = mapToRecord(this._constructorGivensMap);
167
+ const haveAny = fileGivens || constructorGivens || perCall;
168
+ return haveAny
169
+ ? { ...fileGivens, ...constructorGivens, ...perCall }
170
+ : undefined;
171
+ }
144
172
  /**
145
173
  * Surface names of givens locked at the runtime layer (the resolved set
146
174
  * the `config.finalizeGivens` directive produced). Per-query supply for
@@ -481,10 +509,18 @@ class Runtime {
481
509
  * in dependency order and each carries its own `dependsOn`. See
482
510
  * {@link BuildTargets}.
483
511
  *
512
+ * The build generates SQL under this Runtime's givens and `virtualMap`, with
513
+ * `options` layered over them, exactly as a query would; a persisted table
514
+ * is therefore found by the queries that run under the same values. A
515
+ * source whose SQL needs a value this build does not have is reported in
516
+ * `unbuildable`, not thrown.
517
+ *
484
518
  * @param model A compiled model with `##! experimental.persistence`
519
+ * @param options Given values and a `virtualMap` for this build
485
520
  * @return The targets to build, with any annotation parse messages
486
521
  */
487
- async getBuildTargets(model) {
522
+ async getBuildTargets(model, options) {
523
+ var _a;
488
524
  const tagParseLog = [];
489
525
  const walk = (0, build_targets_1.resolvePersistWalk)(model, tagParseLog);
490
526
  const connectionDigests = (0, model_1.mkSafeRecord)();
@@ -497,12 +533,12 @@ class Runtime {
497
533
  connectionDigests[connectionName] = connection.getDigest();
498
534
  }
499
535
  }
500
- return {
501
- connections: (0, build_targets_1.mkBuildTargets)(walk, connectionDigests, {
502
- virtualMap: this.virtualMap,
503
- }),
504
- tagParseLog,
505
- };
536
+ const givens = await this._givensFor(options === null || options === void 0 ? void 0 : options.givens);
537
+ const { connections, unbuildable } = (0, build_targets_1.mkBuildTargets)(walk, connectionDigests, {
538
+ virtualMap: (_a = options === null || options === void 0 ? void 0 : options.virtualMap) !== null && _a !== void 0 ? _a : this.virtualMap,
539
+ resolvedGivens: (0, given_binding_1.bindGivens)(givens, model._modelDef),
540
+ });
541
+ return { connections, tagParseLog, unbuildable };
506
542
  }
507
543
  }
508
544
  exports.Runtime = Runtime;
@@ -1045,31 +1081,8 @@ class QueryMaterializer extends FluentState {
1045
1081
  }
1046
1082
  }
1047
1083
  const virtualMap = (_b = mergedOptions.virtualMap) !== null && _b !== void 0 ? _b : this.runtime.virtualMap;
1048
- // Per-query supply for a finalized given is rejected at API entry
1049
- // — the finalized-givens set is the runtime's "this can't be
1050
- // overridden by the caller" guarantee. Fail before any IO so misuse
1051
- // is loud.
1084
+ const mergedGivens = await this.runtime._givensFor(mergedOptions.givens);
1052
1085
  const finalizedSet = this.runtime._finalizedGivens;
1053
- if (finalizedSet.size > 0 && mergedOptions.givens) {
1054
- for (const name of Object.keys(mergedOptions.givens)) {
1055
- if (finalizedSet.has(name)) {
1056
- throw new model_1.MalloyCompileError(`Cannot supply given '${name}' per-query: it is finalized at ` +
1057
- 'the runtime layer via `config.finalizeGivens`.', 'runtime-given-finalized', undefined);
1058
- }
1059
- }
1060
- }
1061
- // Three layers of givens, merged per-key in precedence order:
1062
- // 1. config.givensURL file (lazy + cached)
1063
- // 2. Runtime constructor `givens:` option
1064
- // 3. Per-query supply via `.run({givens: ...})`
1065
- // Higher-numbered layers win on collision. Each layer is optional;
1066
- // the merge collapses to undefined when all three are absent.
1067
- const fileGivens = await this.runtime._resolveGivens();
1068
- const constructorGivens = mapToRecord(this.runtime._constructorGivens);
1069
- const haveAny = fileGivens || constructorGivens || mergedOptions.givens;
1070
- const mergedGivens = haveAny
1071
- ? { ...fileGivens, ...constructorGivens, ...mergedOptions.givens }
1072
- : undefined;
1073
1086
  // Query-scoped validation: of the givens THIS query references,
1074
1087
  // any that are finalized must have a value somewhere (file or
1075
1088
  // constructor). One config covers a project; individual files
@@ -107,11 +107,23 @@ export interface BuildTarget {
107
107
  * The SQL the BuildID is computed from: fully inlined, no manifest
108
108
  * substitution, so it is the same string whatever else has been built.
109
109
  *
110
- * It is not the SQL to execute. That one substitutes the tables built so far
111
- * — `source.getSQL({buildManifest, connectionDigests})` — so it can only be
112
- * computed as the build walks, and only by the builder holding the manifest.
110
+ * It is not the SQL to execute; that is {@link buildSQL}.
113
111
  */
114
112
  sql: string;
113
+ /**
114
+ * The SQL to execute, for a `CREATE TABLE`. Dependencies already in
115
+ * `manifest` are read from their tables rather than recomputed, which is
116
+ * why it is a call made as the build proceeds and not a string computed up
117
+ * front. Generated under the same givens and `virtualMap` as `sql`, so the
118
+ * table that is built is the one the key describes.
119
+ */
120
+ buildSQL(manifest: BuildManifest): string;
121
+ /**
122
+ * True when the SQL reads a given, supplied or defaulted. The table is then
123
+ * one of possibly several for this source, one per value, and a builder
124
+ * naming it by `name=` alone would build every variant under one name.
125
+ */
126
+ readsGivens: boolean;
115
127
  /** Targets that must exist before this one can be built */
116
128
  dependsOn: BuildTarget[];
117
129
  /** Every persist source in the model that maps onto this artifact */
@@ -152,6 +164,32 @@ export interface ConnectionBuild {
152
164
  /** Every table to build, dependencies before dependents */
153
165
  targets: BuildTarget[];
154
166
  }
167
+ /**
168
+ * A persist source with no BuildID, because its SQL cannot be written: it
169
+ * reads a given that has no value in this build, a virtual source the build's
170
+ * `virtualMap` has no entry for, or anything else the compiler reports as a
171
+ * problem with the source rather than with itself. A source that reads it
172
+ * inlines its SQL, so is reported here too.
173
+ */
174
+ export interface UnbuildableSource {
175
+ source: PersistSource;
176
+ /** Why no BuildID could be computed, as a query would report it */
177
+ problem: LogMessage;
178
+ }
179
+ /**
180
+ * What a build generates SQL under, over the Runtime's own values.
181
+ *
182
+ * `givens` layers over the Runtime's file and constructor givens the way a
183
+ * query's do, and a finalized given is refused here the same way. Running
184
+ * `getBuildTargets` once per set of values is how a model whose persisted
185
+ * sources read a given gets one table per value.
186
+ */
187
+ export interface BuildTargetOptions {
188
+ /** Given values for this build, by surface name, over the Runtime's */
189
+ givens?: Record<string, GivenValue>;
190
+ /** Which table each virtual source reads; the Runtime's when omitted */
191
+ virtualMap?: VirtualMap;
192
+ }
155
193
  /**
156
194
  * The build schedule for a model.
157
195
  *
@@ -165,4 +203,6 @@ export interface BuildTargets {
165
203
  connections: ConnectionBuild[];
166
204
  /** Errors and warnings from parsing `#@` annotations on persistable sources */
167
205
  tagParseLog: LogMessage[];
206
+ /** Persist sources with no table to build, each with the reason */
207
+ unbuildable: UnbuildableSource[];
168
208
  }
@@ -0,0 +1,29 @@
1
+ import type { AnnotationsDef } from '../model/malloy_types';
2
+ import type { ConnectionPropertyDefinition } from './registry';
3
+ /**
4
+ * The annotation route a connection writes database metadata to:
5
+ * `#(dbmeta) description="..."`.
6
+ */
7
+ export declare const DBMETA_ROUTE = "dbmeta";
8
+ /**
9
+ * The connection property that turns on database metadata. Connections that
10
+ * support it register this definition, so the option has the same name and
11
+ * meaning on every backend. Off unless set.
12
+ */
13
+ export declare const dbmetaProperty: ConnectionPropertyDefinition;
14
+ /**
15
+ * Metadata a connection read from the database for one table or column.
16
+ * Keys are normalized across databases: whatever a database calls it (a
17
+ * description, a COMMENT), it arrives here as `description`.
18
+ */
19
+ export interface DbMeta {
20
+ description?: string | null;
21
+ }
22
+ /**
23
+ * The `#(dbmeta)` annotation for a table or column, as a fragment to spread
24
+ * into its field or table def: `{annotations}`, or `{}` when there is nothing
25
+ * to say.
26
+ */
27
+ export declare function dbmetaAnnotations(meta: DbMeta): {
28
+ annotations?: AnnotationsDef;
29
+ };
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.dbmetaProperty = exports.DBMETA_ROUTE = void 0;
8
+ exports.dbmetaAnnotations = dbmetaAnnotations;
9
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
10
+ /**
11
+ * The annotation route a connection writes database metadata to:
12
+ * `#(dbmeta) description="..."`.
13
+ */
14
+ exports.DBMETA_ROUTE = 'dbmeta';
15
+ const DBMETA_LOCATION = {
16
+ url: `internal://${exports.DBMETA_ROUTE}`,
17
+ range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
18
+ };
19
+ /**
20
+ * The connection property that turns on database metadata. Connections that
21
+ * support it register this definition, so the option has the same name and
22
+ * meaning on every backend. Off unless set.
23
+ */
24
+ exports.dbmetaProperty = {
25
+ name: 'dbmeta',
26
+ displayName: 'Database Metadata',
27
+ type: 'boolean',
28
+ optional: true,
29
+ advanced: true,
30
+ description: 'Copy table and column descriptions from the database onto the schema, ' +
31
+ 'as #(dbmeta) description annotations.',
32
+ };
33
+ /**
34
+ * The `#(dbmeta)` annotation for a table or column, as a fragment to spread
35
+ * into its field or table def: `{annotations}`, or `{}` when there is nothing
36
+ * to say.
37
+ */
38
+ function dbmetaAnnotations(meta) {
39
+ var _a;
40
+ const description = (_a = meta.description) === null || _a === void 0 ? void 0 : _a.trim();
41
+ if (!description)
42
+ return {};
43
+ const text = `#(${exports.DBMETA_ROUTE}) description=${(0, malloy_tag_1.quoteString)(description)}\n`;
44
+ return { annotations: { notes: [{ text, at: DBMETA_LOCATION }] } };
45
+ }
46
+ //# sourceMappingURL=dbmeta.js.map
@@ -1,3 +1,4 @@
1
1
  export * from './types';
2
2
  export * from './base_connection';
3
3
  export * from './registry';
4
+ export * from './dbmeta';
@@ -17,4 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./types"), exports);
18
18
  __exportStar(require("./base_connection"), exports);
19
19
  __exportStar(require("./registry"), exports);
20
+ __exportStar(require("./dbmeta"), exports);
20
21
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
- import type { Sampling, MeasureTimeExpr, RegexMatchExpr, TimeExtractExpr, TypecastExpr, BasicAtomicTypeDef, AtomicTypeDef, ArrayLiteralNode, RecordLiteralNode } from '../../model/malloy_types';
2
- import type { BooleanTypeSupport, CompiledOrderBy, DialectFieldList, FieldReferenceType, LateralJoinExpression, OrderByClauseType, OrderByRequest, QueryInfo } from '../dialect';
1
+ import type { Sampling, MeasureTimeExpr, RegexMatchExpr, TypecastExpr, BasicAtomicTypeDef, AtomicTypeDef, ArrayLiteralNode, RecordLiteralNode, ATimestampTypeDef, TimestampUnit, ExtractUnit } from '../../model/malloy_types';
2
+ import type { CivilTime, BooleanTypeSupport, CompiledOrderBy, DialectFieldList, FieldReferenceType, LateralJoinExpression, OrderByClauseType, OrderByRequest, QueryInfo } from '../dialect';
3
3
  import { Dialect } from '../dialect';
4
4
  import type { DialectFunctionOverloadDef } from '../functions';
5
5
  export declare class DatabricksDialect extends Dialect {
@@ -66,18 +66,17 @@ export declare class DatabricksDialect extends Dialect {
66
66
  sqlSelectAliasAsStruct(alias: string, fieldList: DialectFieldList): string;
67
67
  sqlCreateTableAsSelect(tableName: string, sql: string): string;
68
68
  sqlNowExpr(): string;
69
- sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: AtomicTypeDef): {
70
- sql: string;
71
- typeDef: AtomicTypeDef;
72
- };
73
- sqlConvertFromCivilTime(expr: string, timezone: string, _destTypeDef: AtomicTypeDef): string;
74
- sqlTruncate(expr: string, unit: string, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
75
- sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: string, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
76
- sqlTimeExtractExpr(qi: QueryInfo, te: TimeExtractExpr): string;
77
- sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
69
+ sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: ATimestampTypeDef): string;
70
+ sqlConvertFromCivilTime(expr: string, timezone: string, _destTypeDef: ATimestampTypeDef): string;
71
+ sqlTruncate(expr: string, unit: TimestampUnit, _civilType: CivilTime): string;
72
+ sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _civilType: CivilTime): string;
73
+ sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
74
+ sqlCivilToDate(expr: string): string;
75
+ sqlDateToCivil(expr: string): string;
76
+ sqlCast(_qi: QueryInfo, cast: TypecastExpr): string;
78
77
  sqlRegexpMatch(df: RegexMatchExpr): string;
79
78
  sqlDateLiteral(_qi: QueryInfo, literal: string): string;
80
- sqlTimestampLiteral(qi: QueryInfo, literal: string, timezone: string | undefined): string;
79
+ sqlCivilLiteral(literal: string): string;
81
80
  sqlTimestamptzLiteral(_qi: QueryInfo, _literal: string, _timezone: string): string;
82
81
  sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
83
82
  sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;