@malloydata/malloy 0.0.427 → 0.0.429

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 (129) hide show
  1. package/dist/api/foundation/build_targets.d.ts +50 -0
  2. package/dist/api/foundation/build_targets.js +221 -0
  3. package/dist/api/foundation/core.d.ts +54 -10
  4. package/dist/api/foundation/core.js +80 -17
  5. package/dist/api/foundation/index.d.ts +1 -1
  6. package/dist/api/foundation/runtime.d.ts +18 -1
  7. package/dist/api/foundation/runtime.js +35 -0
  8. package/dist/api/foundation/types.d.ts +93 -6
  9. package/dist/index.d.ts +1 -1
  10. package/dist/lang/ast/expressions/apply.d.ts +1 -1
  11. package/dist/lang/ast/expressions/apply.js +2 -2
  12. package/dist/lang/ast/expressions/binary-boolean.d.ts +1 -1
  13. package/dist/lang/ast/expressions/binary-boolean.js +1 -1
  14. package/dist/lang/ast/expressions/binary-numeric.d.ts +1 -1
  15. package/dist/lang/ast/expressions/binary-numeric.js +1 -1
  16. package/dist/lang/ast/expressions/boolean.d.ts +1 -1
  17. package/dist/lang/ast/expressions/boolean.js +1 -1
  18. package/dist/lang/ast/expressions/case.d.ts +1 -1
  19. package/dist/lang/ast/expressions/case.js +1 -1
  20. package/dist/lang/ast/expressions/constant-expression.d.ts +2 -1
  21. package/dist/lang/ast/expressions/constant-expression.js +6 -1
  22. package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +1 -1
  23. package/dist/lang/ast/expressions/expr-aggregate-function.js +14 -7
  24. package/dist/lang/ast/expressions/expr-alternation-tree.d.ts +1 -1
  25. package/dist/lang/ast/expressions/expr-alternation-tree.js +1 -1
  26. package/dist/lang/ast/expressions/expr-array-literal.d.ts +1 -1
  27. package/dist/lang/ast/expressions/expr-array-literal.js +1 -1
  28. package/dist/lang/ast/expressions/expr-cast.d.ts +1 -1
  29. package/dist/lang/ast/expressions/expr-cast.js +1 -1
  30. package/dist/lang/ast/expressions/expr-coalesce.d.ts +1 -1
  31. package/dist/lang/ast/expressions/expr-coalesce.js +1 -1
  32. package/dist/lang/ast/expressions/expr-compare.d.ts +4 -4
  33. package/dist/lang/ast/expressions/expr-compare.js +4 -4
  34. package/dist/lang/ast/expressions/expr-count.d.ts +1 -1
  35. package/dist/lang/ast/expressions/expr-count.js +1 -1
  36. package/dist/lang/ast/expressions/expr-filter-expr.d.ts +1 -1
  37. package/dist/lang/ast/expressions/expr-filter-expr.js +1 -1
  38. package/dist/lang/ast/expressions/expr-func.d.ts +1 -1
  39. package/dist/lang/ast/expressions/expr-func.js +1 -1
  40. package/dist/lang/ast/expressions/expr-given.d.ts +1 -1
  41. package/dist/lang/ast/expressions/expr-given.js +1 -1
  42. package/dist/lang/ast/expressions/expr-granular-time.d.ts +1 -1
  43. package/dist/lang/ast/expressions/expr-granular-time.js +1 -1
  44. package/dist/lang/ast/expressions/expr-id-reference.d.ts +1 -1
  45. package/dist/lang/ast/expressions/expr-id-reference.js +1 -1
  46. package/dist/lang/ast/expressions/expr-minus.d.ts +1 -1
  47. package/dist/lang/ast/expressions/expr-minus.js +1 -1
  48. package/dist/lang/ast/expressions/expr-not.d.ts +1 -1
  49. package/dist/lang/ast/expressions/expr-not.js +1 -1
  50. package/dist/lang/ast/expressions/expr-now.d.ts +1 -1
  51. package/dist/lang/ast/expressions/expr-now.js +1 -1
  52. package/dist/lang/ast/expressions/expr-null.d.ts +3 -3
  53. package/dist/lang/ast/expressions/expr-null.js +14 -9
  54. package/dist/lang/ast/expressions/expr-number.d.ts +1 -1
  55. package/dist/lang/ast/expressions/expr-number.js +1 -1
  56. package/dist/lang/ast/expressions/expr-parens.d.ts +1 -1
  57. package/dist/lang/ast/expressions/expr-parens.js +5 -2
  58. package/dist/lang/ast/expressions/expr-props.d.ts +1 -1
  59. package/dist/lang/ast/expressions/expr-props.js +1 -1
  60. package/dist/lang/ast/expressions/expr-record-literal.d.ts +1 -1
  61. package/dist/lang/ast/expressions/expr-record-literal.js +1 -1
  62. package/dist/lang/ast/expressions/expr-regex.d.ts +1 -1
  63. package/dist/lang/ast/expressions/expr-regex.js +1 -1
  64. package/dist/lang/ast/expressions/expr-string.d.ts +1 -1
  65. package/dist/lang/ast/expressions/expr-string.js +1 -1
  66. package/dist/lang/ast/expressions/expr-time-extract.d.ts +1 -1
  67. package/dist/lang/ast/expressions/expr-time-extract.js +1 -1
  68. package/dist/lang/ast/expressions/expr-time.d.ts +1 -1
  69. package/dist/lang/ast/expressions/expr-time.js +1 -1
  70. package/dist/lang/ast/expressions/expr-ungroup.d.ts +1 -1
  71. package/dist/lang/ast/expressions/expr-ungroup.js +1 -1
  72. package/dist/lang/ast/expressions/for-range.d.ts +1 -1
  73. package/dist/lang/ast/expressions/for-range.js +1 -1
  74. package/dist/lang/ast/expressions/partial-compare.d.ts +1 -1
  75. package/dist/lang/ast/expressions/partial-compare.js +1 -1
  76. package/dist/lang/ast/expressions/pick-when.d.ts +1 -1
  77. package/dist/lang/ast/expressions/pick-when.js +1 -1
  78. package/dist/lang/ast/expressions/range.d.ts +1 -1
  79. package/dist/lang/ast/expressions/range.js +1 -1
  80. package/dist/lang/ast/expressions/time-literal.d.ts +2 -2
  81. package/dist/lang/ast/expressions/time-literal.js +2 -2
  82. package/dist/lang/ast/field-space/dynamic-space.js +20 -3
  83. package/dist/lang/ast/field-space/parameter-space.d.ts +1 -0
  84. package/dist/lang/ast/field-space/parameter-space.js +6 -0
  85. package/dist/lang/ast/field-space/passthrough-space.d.ts +1 -0
  86. package/dist/lang/ast/field-space/passthrough-space.js +3 -0
  87. package/dist/lang/ast/field-space/static-space.d.ts +8 -0
  88. package/dist/lang/ast/field-space/static-space.js +14 -0
  89. package/dist/lang/ast/query-builders/index-builder.js +1 -1
  90. package/dist/lang/ast/query-elements/query-arrow.d.ts +1 -1
  91. package/dist/lang/ast/query-elements/query-arrow.js +10 -4
  92. package/dist/lang/ast/query-elements/query-base.d.ts +1 -1
  93. package/dist/lang/ast/query-elements/query-base.js +1 -6
  94. package/dist/lang/ast/query-elements/query-raw.d.ts +1 -1
  95. package/dist/lang/ast/query-elements/query-raw.js +4 -2
  96. package/dist/lang/ast/query-elements/query-reference.d.ts +1 -1
  97. package/dist/lang/ast/query-elements/query-reference.js +9 -3
  98. package/dist/lang/ast/query-elements/query-refine.d.ts +1 -1
  99. package/dist/lang/ast/query-elements/query-refine.js +10 -4
  100. package/dist/lang/ast/query-properties/drill.d.ts +1 -1
  101. package/dist/lang/ast/query-properties/drill.js +1 -1
  102. package/dist/lang/ast/source-elements/query-source.js +1 -1
  103. package/dist/lang/ast/source-elements/sql-source.js +2 -2
  104. package/dist/lang/ast/source-query-elements/source-query-element.d.ts +27 -0
  105. package/dist/lang/ast/source-query-elements/source-query-element.js +33 -0
  106. package/dist/lang/ast/source-query-elements/sq-arrow.d.ts +2 -0
  107. package/dist/lang/ast/source-query-elements/sq-arrow.js +12 -6
  108. package/dist/lang/ast/source-query-elements/sq-refine.d.ts +2 -0
  109. package/dist/lang/ast/source-query-elements/sq-refine.js +12 -6
  110. package/dist/lang/ast/source-query-elements/sq-source.d.ts +1 -0
  111. package/dist/lang/ast/source-query-elements/sq-source.js +6 -3
  112. package/dist/lang/ast/sql-elements/sql-string.d.ts +1 -1
  113. package/dist/lang/ast/sql-elements/sql-string.js +25 -5
  114. package/dist/lang/ast/statements/import-statement.js +7 -10
  115. package/dist/lang/ast/types/expression-def.d.ts +50 -23
  116. package/dist/lang/ast/types/expression-def.js +127 -65
  117. package/dist/lang/ast/types/field-space.d.ts +14 -0
  118. package/dist/lang/ast/types/field-space.js +36 -0
  119. package/dist/lang/ast/types/query-element.d.ts +15 -1
  120. package/dist/lang/parse-log.d.ts +2 -0
  121. package/dist/lang/test/test-translator.d.ts +17 -0
  122. package/dist/lang/test/test-translator.js +46 -2
  123. package/dist/model/malloy_types.d.ts +0 -13
  124. package/dist/model/malloy_types.js +0 -19
  125. package/dist/model/persist_utils.d.ts +61 -10
  126. package/dist/model/persist_utils.js +172 -63
  127. package/dist/version.d.ts +1 -1
  128. package/dist/version.js +1 -1
  129. package/package.json +4 -4
@@ -21,7 +21,9 @@ class QueryRaw extends malloy_element_1.MalloyElement {
21
21
  this.source = source;
22
22
  this.elementType = 'query-raw';
23
23
  }
24
- queryComp(isRefOk) {
24
+ // A raw segment has no query class to decide, so it is never partial and
25
+ // `isPartialOk` has nothing to say here.
26
+ queryComp(isRefOk, _isPartialOk) {
25
27
  const invoked = isRefOk
26
28
  ? this.source.structRef(undefined)
27
29
  : { structRef: this.source.getSourceDef(undefined) };
@@ -40,7 +42,7 @@ class QueryRaw extends malloy_element_1.MalloyElement {
40
42
  };
41
43
  }
42
44
  query(isRefOk = true) {
43
- return this.queryComp(isRefOk).query;
45
+ return this.queryComp(isRefOk, false).query;
44
46
  }
45
47
  }
46
48
  exports.QueryRaw = QueryRaw;
@@ -12,6 +12,6 @@ export declare class QueryReference extends MalloyElement implements QueryElemen
12
12
  readonly name: ModelEntryReference;
13
13
  elementType: string;
14
14
  constructor(name: ModelEntryReference);
15
- queryComp(isRefOk: boolean): QueryComp;
15
+ queryComp(isRefOk: boolean, isPartialOk: boolean): QueryComp;
16
16
  query(isRefOk?: boolean): Query;
17
17
  }
@@ -8,6 +8,7 @@ exports.QueryReference = void 0;
8
8
  const error_factory_1 = require("../error-factory");
9
9
  const malloy_element_1 = require("../types/malloy-element");
10
10
  const query_head_struct_1 = require("./query-head-struct");
11
+ const query_utils_1 = require("../query-utils");
11
12
  const malloy_types_1 = require("../../../model/malloy_types");
12
13
  /**
13
14
  * A query operation that is just a reference to an existing query.
@@ -20,7 +21,7 @@ class QueryReference extends malloy_element_1.MalloyElement {
20
21
  this.name = name;
21
22
  this.elementType = 'query-reference';
22
23
  }
23
- queryComp(isRefOk) {
24
+ queryComp(isRefOk, isPartialOk) {
24
25
  const headEntry = this.modelEntry(this.name);
25
26
  const query = headEntry === null || headEntry === void 0 ? void 0 : headEntry.entry;
26
27
  const oops = function () {
@@ -45,7 +46,12 @@ class QueryReference extends malloy_element_1.MalloyElement {
45
46
  ? query
46
47
  : { ...query, structRef: inputStruct };
47
48
  return {
48
- query: unRefedQuery,
49
+ query: isPartialOk
50
+ ? unRefedQuery
51
+ : {
52
+ ...unRefedQuery,
53
+ pipeline: (0, query_utils_1.detectAndRemovePartialStages)(unRefedQuery.pipeline, this),
54
+ },
49
55
  outputStruct,
50
56
  inputStruct,
51
57
  };
@@ -54,7 +60,7 @@ class QueryReference extends malloy_element_1.MalloyElement {
54
60
  return oops();
55
61
  }
56
62
  query(isRefOk = true) {
57
- return this.queryComp(isRefOk).query;
63
+ return this.queryComp(isRefOk, false).query;
58
64
  }
59
65
  }
60
66
  exports.QueryReference = QueryReference;
@@ -12,5 +12,5 @@ export declare class QueryRefine extends QueryBase implements QueryElement {
12
12
  readonly refinement: View;
13
13
  elementType: string;
14
14
  constructor(base: QueryElement, refinement: View);
15
- queryComp(isRefOk: boolean): QueryComp;
15
+ queryComp(isRefOk: boolean, isPartialOk: boolean): QueryComp;
16
16
  }
@@ -8,6 +8,7 @@ exports.QueryRefine = void 0;
8
8
  const static_space_1 = require("../field-space/static-space");
9
9
  const query_base_1 = require("./query-base");
10
10
  const composite_source_utils_1 = require("../../composite-source-utils");
11
+ const query_utils_1 = require("../query-utils");
11
12
  /**
12
13
  * A query operation that consists of an exisitng query with refinements.
13
14
  *
@@ -20,8 +21,10 @@ class QueryRefine extends query_base_1.QueryBase {
20
21
  this.refinement = refinement;
21
22
  this.elementType = 'query-refine';
22
23
  }
23
- queryComp(isRefOk) {
24
- const q = this.base.queryComp(isRefOk);
24
+ queryComp(isRefOk, isPartialOk) {
25
+ // The refinement is what decides the query class, so the base is the one
26
+ // place a partial segment is expected rather than an error.
27
+ const q = this.base.queryComp(isRefOk, true);
25
28
  const inputFS = new static_space_1.StaticSourceSpace(q.inputStruct, 'public');
26
29
  const pipeline = this.refinement.refine(inputFS, q.query.pipeline, undefined);
27
30
  const query = {
@@ -30,12 +33,15 @@ class QueryRefine extends query_base_1.QueryBase {
30
33
  };
31
34
  const compositeResolvedSourceDef = this.resolveCompositeSource(q.inputStruct, pipeline);
32
35
  const pipelineWithExpandedFieldUsage = this.expandRefUsage(compositeResolvedSourceDef !== null && compositeResolvedSourceDef !== void 0 ? compositeResolvedSourceDef : q.inputStruct, pipeline);
36
+ const finalPipeline = isPartialOk
37
+ ? pipelineWithExpandedFieldUsage
38
+ : (0, query_utils_1.detectAndRemovePartialStages)(pipelineWithExpandedFieldUsage, this);
33
39
  return {
34
40
  query: {
35
41
  ...query,
36
42
  compositeResolvedSourceDef,
37
- pipeline: pipelineWithExpandedFieldUsage,
38
- givenUsage: (0, composite_source_utils_1.computeQueryGivenUsage)(pipelineWithExpandedFieldUsage),
43
+ pipeline: finalPipeline,
44
+ givenUsage: (0, composite_source_utils_1.computeQueryGivenUsage)(finalPipeline),
39
45
  },
40
46
  // TODO bleh
41
47
  outputStruct: pipeline[pipeline.length - 1].outputStruct,
@@ -13,7 +13,7 @@ export declare class DrillField extends ExpressionDef {
13
13
  readonly typeDesc: TypeDesc;
14
14
  elementType: string;
15
15
  constructor(expr: Expr, typeDesc: TypeDesc);
16
- getExpression(_fs: FieldSpace): ExprValue;
16
+ protected computeExpression(_fs: FieldSpace): ExprValue;
17
17
  }
18
18
  export declare class Drill extends Filter implements QueryPropertyInterface {
19
19
  elementType: string;
@@ -25,7 +25,7 @@ class DrillField extends expression_def_1.ExpressionDef {
25
25
  this.typeDesc = typeDesc;
26
26
  this.elementType = 'drillField';
27
27
  }
28
- getExpression(_fs) {
28
+ computeExpression(_fs) {
29
29
  return {
30
30
  value: this.expr,
31
31
  ...this.typeDesc,
@@ -18,7 +18,7 @@ class QuerySource extends source_1.Source {
18
18
  return this.withParameters(parameterSpace, undefined);
19
19
  }
20
20
  withParameters(parameterSpace, pList) {
21
- const comp = this.query.queryComp(false);
21
+ const comp = this.query.queryComp(false, false);
22
22
  const queryStruct = (0, source_def_utils_1.mkQuerySourceDef)(comp.outputStruct, comp.query, `QuerySource-${(0, uuid_1.v4)()}`);
23
23
  return {
24
24
  ...queryStruct,
@@ -22,7 +22,7 @@ class SQLSource extends source_1.Source {
22
22
  const partialModel = this.select.containsQueries
23
23
  ? doc.modelDef()
24
24
  : undefined;
25
- const [valid, phrases] = this.select.sqlPhrases();
25
+ const [valid, phrases] = this.select.sqlPhrases(this.connectionName.refString);
26
26
  if (valid) {
27
27
  return (0, sql_block_1.getSourceRequest)(phrases, this.connectionName.refString, partialModel);
28
28
  }
@@ -123,7 +123,7 @@ class SQLSource extends source_1.Source {
123
123
  location: this.location,
124
124
  };
125
125
  // Use factory to create SQLSourceDef without propagating sourceID/extends
126
- const [_valid, phrases] = this.select.sqlPhrases();
126
+ const [_valid, phrases] = this.select.sqlPhrases(this.connectionName.refString);
127
127
  const selectSegments = this.select.containsQueries ? phrases : undefined;
128
128
  const locStruct = (0, source_def_utils_1.mkSQLSourceDef)(baseStruct, lookup.value.selectStr, selectSegments);
129
129
  return locStruct;
@@ -9,10 +9,37 @@ import type { LogMessageOptions, MessageCode, MessageParameterType } from '../..
9
9
  * a query.
10
10
  */
11
11
  export declare abstract class SourceQueryElement extends MalloyElement {
12
+ /** Set once this element has reported why it could not produce a value. */
12
13
  errored: boolean;
13
14
  getSource(): Source | undefined;
14
15
  getQuery(): QueryElement | undefined;
15
16
  isSource(): boolean;
17
+ /**
18
+ * Report the one message which explains why this expression could not be
19
+ * turned into a source or a query.
20
+ *
21
+ * Every element in a source/query expression says something when it fails,
22
+ * so an outer element's generic complaint ("could not get source for
23
+ * query") would stack on top of the specific cause already reported
24
+ * beneath it. The message is therefore logged only when nothing in this
25
+ * subtree has spoken yet, and `errored` is set either way, which is what
26
+ * silences the elements above.
27
+ *
28
+ * This is not protection against logging the same message twice —
29
+ * `MalloyElement.log` already drops a repeat of one message at one
30
+ * location. It is one message per failed expression.
31
+ *
32
+ * The case it does not serve is two unrelated complaints about the same
33
+ * element: a second `sqLog` is dropped. Use `sqClaimError` for those.
34
+ */
16
35
  sqLog<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: LogMessageOptions): T;
36
+ /**
37
+ * Take the one error report this expression is allowed, for an element with
38
+ * more than one complaint to make. False means something below has already
39
+ * spoken. True means the caller owns the report and should `logError` each
40
+ * of its complaints; nothing above will speak after that.
41
+ */
42
+ sqClaimError(): boolean;
43
+ /** True until this element, or anything below it, has reported a failure. */
17
44
  isErrorFree(): boolean;
18
45
  }
@@ -15,6 +15,7 @@ const malloy_element_1 = require("../types/malloy-element");
15
15
  class SourceQueryElement extends malloy_element_1.MalloyElement {
16
16
  constructor() {
17
17
  super(...arguments);
18
+ /** Set once this element has reported why it could not produce a value. */
18
19
  this.errored = false;
19
20
  }
20
21
  getSource() {
@@ -26,6 +27,24 @@ class SourceQueryElement extends malloy_element_1.MalloyElement {
26
27
  isSource() {
27
28
  return false;
28
29
  }
30
+ /**
31
+ * Report the one message which explains why this expression could not be
32
+ * turned into a source or a query.
33
+ *
34
+ * Every element in a source/query expression says something when it fails,
35
+ * so an outer element's generic complaint ("could not get source for
36
+ * query") would stack on top of the specific cause already reported
37
+ * beneath it. The message is therefore logged only when nothing in this
38
+ * subtree has spoken yet, and `errored` is set either way, which is what
39
+ * silences the elements above.
40
+ *
41
+ * This is not protection against logging the same message twice —
42
+ * `MalloyElement.log` already drops a repeat of one message at one
43
+ * location. It is one message per failed expression.
44
+ *
45
+ * The case it does not serve is two unrelated complaints about the same
46
+ * element: a second `sqLog` is dropped. Use `sqClaimError` for those.
47
+ */
29
48
  sqLog(code, parameters, options) {
30
49
  if (this.isErrorFree()) {
31
50
  this.logError(code, parameters, options);
@@ -33,6 +52,20 @@ class SourceQueryElement extends malloy_element_1.MalloyElement {
33
52
  this.errored = true;
34
53
  return code;
35
54
  }
55
+ /**
56
+ * Take the one error report this expression is allowed, for an element with
57
+ * more than one complaint to make. False means something below has already
58
+ * spoken. True means the caller owns the report and should `logError` each
59
+ * of its complaints; nothing above will speak after that.
60
+ */
61
+ sqClaimError() {
62
+ if (!this.isErrorFree()) {
63
+ return false;
64
+ }
65
+ this.errored = true;
66
+ return true;
67
+ }
68
+ /** True until this element, or anything below it, has reported a failure. */
36
69
  isErrorFree() {
37
70
  if (this.errored) {
38
71
  return false;
@@ -16,6 +16,8 @@ export declare class SQArrow extends SourceQueryElement {
16
16
  readonly applyTo: SourceQueryElement;
17
17
  readonly operation: View;
18
18
  elementType: string;
19
+ asQuery?: QueryElement;
20
+ asSource?: Source;
19
21
  constructor(applyTo: SourceQueryElement, operation: View);
20
22
  getQuery(): QueryElement | undefined;
21
23
  getSource(): Source | undefined;
@@ -26,6 +26,9 @@ class SQArrow extends source_query_element_1.SourceQueryElement {
26
26
  this.elementType = 'sq-arrow';
27
27
  }
28
28
  getQuery() {
29
+ if (this.asQuery) {
30
+ return this.asQuery;
31
+ }
29
32
  const lhs = this.applyTo.isSource()
30
33
  ? this.applyTo.getSource()
31
34
  : this.applyTo.getQuery();
@@ -33,19 +36,22 @@ class SQArrow extends source_query_element_1.SourceQueryElement {
33
36
  this.sqLog('failed-to-compute-arrow-source', 'Could not get LHS of arrow operation');
34
37
  return;
35
38
  }
36
- const arr = new query_arrow_1.QueryArrow(lhs, this.operation);
37
- this.has({ query: arr });
38
- return arr;
39
+ this.asQuery = new query_arrow_1.QueryArrow(lhs, this.operation);
40
+ this.has({ query: this.asQuery });
41
+ return this.asQuery;
39
42
  }
40
43
  getSource() {
44
+ if (this.asSource) {
45
+ return this.asSource;
46
+ }
41
47
  const query = this.getQuery();
42
48
  if (!query) {
43
49
  this.sqLog('failed-to-compute-source-from-query', "Couldn't comprehend query well enough to make a source");
44
50
  return;
45
51
  }
46
- const asSource = new query_source_1.QuerySource(query);
47
- this.has({ asSource });
48
- return asSource;
52
+ this.asSource = new query_source_1.QuerySource(query);
53
+ this.has({ asSource: this.asSource });
54
+ return this.asSource;
49
55
  }
50
56
  }
51
57
  exports.SQArrow = SQArrow;
@@ -12,6 +12,8 @@ export declare class SQRefine extends SourceQueryElement {
12
12
  readonly toRefine: SourceQueryElement;
13
13
  readonly refine: View;
14
14
  elementType: string;
15
+ asQuery?: QueryRefine;
16
+ asSource?: QuerySource;
15
17
  constructor(toRefine: SourceQueryElement, refine: View);
16
18
  getQuery(): QueryRefine | undefined;
17
19
  getSource(): QuerySource | undefined;
@@ -23,6 +23,9 @@ class SQRefine extends source_query_element_1.SourceQueryElement {
23
23
  this.elementType = 'sq-refine';
24
24
  }
25
25
  getQuery() {
26
+ if (this.asQuery) {
27
+ return this.asQuery;
28
+ }
26
29
  if (this.toRefine.isSource()) {
27
30
  if (this.toRefine instanceof sq_reference_1.SQReference) {
28
31
  this.sqLog('illegal-refinement-of-source', `Cannot add view refinements to '${this.toRefine.ref.refString}' because it is a source`);
@@ -34,17 +37,20 @@ class SQRefine extends source_query_element_1.SourceQueryElement {
34
37
  }
35
38
  const refinedQuery = this.toRefine.getQuery();
36
39
  if (refinedQuery) {
37
- const resultQuery = new query_refine_1.QueryRefine(refinedQuery, this.refine);
38
- this.has({ query: resultQuery });
39
- return resultQuery;
40
+ this.asQuery = new query_refine_1.QueryRefine(refinedQuery, this.refine);
41
+ this.has({ query: this.asQuery });
42
+ return this.asQuery;
40
43
  }
41
44
  }
42
45
  getSource() {
46
+ if (this.asSource) {
47
+ return this.asSource;
48
+ }
43
49
  const query = this.getQuery();
44
50
  if (query) {
45
- const queryAsSource = new query_source_1.QuerySource(query);
46
- this.has({ queryAsSource });
47
- return queryAsSource;
51
+ this.asSource = new query_source_1.QuerySource(query);
52
+ this.has({ queryAsSource: this.asSource });
53
+ return this.asSource;
48
54
  }
49
55
  }
50
56
  }
@@ -10,6 +10,7 @@ import { QueryRaw } from '../query-elements/query-raw';
10
10
  export declare class SQSource extends SourceQueryElement {
11
11
  readonly theSource: Source;
12
12
  elementType: string;
13
+ asQuery?: QueryRaw;
13
14
  constructor(theSource: Source);
14
15
  isSource(): boolean;
15
16
  getSource(): Source;
@@ -27,10 +27,13 @@ class SQSource extends source_query_element_1.SourceQueryElement {
27
27
  return this.theSource;
28
28
  }
29
29
  getQuery() {
30
+ if (this.asQuery) {
31
+ return this.asQuery;
32
+ }
30
33
  if (this.theSource instanceof sql_source_1.SQLSource) {
31
- const rawQuery = new query_raw_1.QueryRaw(this.theSource);
32
- this.has({ rawQuery });
33
- return rawQuery;
34
+ this.asQuery = new query_raw_1.QueryRaw(this.theSource);
35
+ this.has({ rawQuery: this.asQuery });
36
+ return this.asQuery;
34
37
  }
35
38
  else {
36
39
  this.sqLog('invalid-source-as-query', 'This source cannot be used as a query');
@@ -8,6 +8,6 @@ export declare class SQLString extends MalloyElement {
8
8
  containsQueries: boolean;
9
9
  complete(): void;
10
10
  push(el: string | MalloyElement): void;
11
- sqlPhrases(): [boolean, SQLPhraseSegment[]];
11
+ sqlPhrases(forConnection: string): [boolean, SQLPhraseSegment[]];
12
12
  }
13
13
  export {};
@@ -35,7 +35,7 @@ class SQLString extends malloy_element_1.MalloyElement {
35
35
  el.logError('invalid-sql-source-interpolation', 'This element is not legal inside an SQL string');
36
36
  }
37
37
  }
38
- sqlPhrases() {
38
+ sqlPhrases(forConnection) {
39
39
  const ret = [];
40
40
  let valid = true;
41
41
  for (const el of this.elements) {
@@ -47,19 +47,39 @@ class SQLString extends malloy_element_1.MalloyElement {
47
47
  const source = el.getSource();
48
48
  if (source) {
49
49
  const sourceDef = source.getSourceDef(undefined);
50
- if ((0, malloy_types_1.isPersistableSourceDef)(sourceDef)) {
50
+ const connectionMismatch = sourceDef.connection !== forConnection;
51
+ const persistable = (0, malloy_types_1.isPersistableSourceDef)(sourceDef);
52
+ if (!connectionMismatch && persistable) {
51
53
  ret.push(sourceDef);
52
54
  continue;
53
55
  }
56
+ valid = false;
57
+ // A source can be wrong in both ways at once, and both are worth
58
+ // saying, so claim the report and then say everything.
59
+ if (el.sqClaimError()) {
60
+ if (connectionMismatch) {
61
+ el.logError('sql-source-connection-mismatch', `Source is on connection ${sourceDef.connection}, but this SQL is for ${forConnection}`);
62
+ }
63
+ if (!persistable) {
64
+ el.logError('invalid-sql-source-interpolation', 'Source is not persistable, cannot be used in SQL');
65
+ }
66
+ }
67
+ }
68
+ else {
69
+ el.sqLog('failed-to-expand-sql-source', 'Cannot expand into a query');
70
+ valid = false;
54
71
  }
55
- el.sqLog('failed-to-expand-sql-source', 'Cannot expand into a query');
56
- valid = false;
57
72
  }
58
73
  else {
59
74
  // Not a source - try as a query
60
75
  const queryObject = el.getQuery();
61
76
  if (queryObject) {
62
- ret.push(queryObject.query(false));
77
+ const queryComp = queryObject.queryComp(false, false);
78
+ if (queryComp.inputStruct.connection !== forConnection) {
79
+ el.sqLog('sql-source-connection-mismatch', `Query is on connection ${queryComp.inputStruct.connection}, but this SQL is for ${forConnection}`);
80
+ valid = false;
81
+ }
82
+ ret.push(queryComp.query);
63
83
  }
64
84
  else {
65
85
  el.sqLog('failed-to-expand-sql-source', 'Cannot expand into a query');
@@ -10,13 +10,6 @@ const malloy_types_1 = require("../../../model/malloy_types");
10
10
  const source_def_utils_1 = require("../../../model/source_def_utils");
11
11
  const persist_utils_1 = require("../../../model/persist_utils");
12
12
  const utils_1 = require("../../../model/utils");
13
- /** Walk BuildNode tree and collect all sourceIDs */
14
- function collectSourceIDs(nodes, into) {
15
- for (const node of nodes) {
16
- into.add(node.sourceID);
17
- collectSourceIDs(node.dependsOn, into);
18
- }
19
- }
20
13
  class ImportSourceName extends malloy_element_1.MalloyElement {
21
14
  constructor(text) {
22
15
  super();
@@ -160,10 +153,14 @@ class ImportStatement extends malloy_element_1.ListOf {
160
153
  const importMe = { ...sourceEntry };
161
154
  importMe.as = dstName;
162
155
  doc.setEntry(dstName, { entry: importMe, exported: false });
163
- // Collect dependencies for persistable sources
156
+ // Every source the walk touched, routes included — not just the
157
+ // persistent ones. A `#@ -persist` wrapper that adds a join is
158
+ // not a table, but it can be the only way to reach the tables
159
+ // under it, and this model has to be able to take the same route.
164
160
  if ((0, malloy_types_1.isSourceDef)(importMe) && (0, malloy_types_1.isPersistableSourceDef)(importMe)) {
165
- const deps = (0, persist_utils_1.findPersistentDependencies)(importMe, importedModel);
166
- collectSourceIDs(deps, neededSourceIDs);
161
+ for (const found of (0, persist_utils_1.walkPersistentDependencies)([importMe], importedModel)) {
162
+ neededSourceIDs.add(found.sourceID);
163
+ }
167
164
  }
168
165
  }
169
166
  }
@@ -13,20 +13,53 @@ export declare enum ATNodeType {
13
13
  }
14
14
  /**
15
15
  * Root node for any element in an expression. These essentially
16
- * create a sub-tree in the larger AST. Expression nodes know
17
- * how to write themselves as SQL (or rather, generate the
18
- * template for SQL required by the query writer)
16
+ * create a sub-tree in the larger AST. An ExpressionDef, when
17
+ * given a FieldSpace, can be evaluated to produce an ExprValue
18
+ * which is the IR for an Expr along with its type and other metadata.
19
19
  */
20
20
  export declare abstract class ExpressionDef extends MalloyElement {
21
21
  abstract elementType: string;
22
22
  granular(): boolean;
23
23
  /**
24
- * Returns the "translation" or template for SQL generation. When asking
24
+ * Returns the "translation" or Expr tree for SQL generation. When asking
25
25
  * for a translation you may pass the types you can accept, allowing
26
26
  * the translation code a chance to convert to match your expectations
27
27
  * @param space Namespace for looking up field references
28
+ *
29
+ * THE RETURNED ExprValue IS SHARED -- TREAT IT AS IMMUTABLE. Asking twice
30
+ * yields the same object, so to derive a value from one, build a new one:
31
+ * `{...operand, type: 'boolean'}` or `computedExprValue({…, from: [operand]})`.
32
+ *
33
+ * DO NOT OVERRIDE THIS. Nodes implement `computeExpression`; an override
34
+ * here bypasses the memo, which for `+`/`-` chains is the difference
35
+ * between linear and exponential compile time.
28
36
  */
29
- abstract getExpression(fs: FieldSpace): ExprValue;
37
+ getExpression(fs: FieldSpace): ExprValue;
38
+ /**
39
+ * The last value this node computed, and what it was computed against.
40
+ * Three states, distinguished by memoGen:
41
+ *
42
+ * memoFs undefined -- nothing computed, or abandoned
43
+ * memoFs set, memoGen undefined -- computing right now, and only that,
44
+ * since abandoning clears memoFs
45
+ * memoFs set, memoGen set -- memoValue is that computation's result
46
+ *
47
+ * Both halves of the stamp are needed: a node can be evaluated under more
48
+ * than one field space -- ConstantExpression evaluates its child against a
49
+ * ConstantFieldSpace whatever it was handed -- and a field space can rebind
50
+ * a name under us.
51
+ *
52
+ * One slot, not a map. A node which alternates between two field spaces
53
+ * simply misses and recomputes, which costs time and never correctness.
54
+ */
55
+ private memoFs?;
56
+ private memoGen?;
57
+ private memoValue?;
58
+ /**
59
+ * Evaluate this node. Implemented by every expression node; called only
60
+ * by `getExpression` above, never directly.
61
+ */
62
+ protected abstract computeExpression(fs: FieldSpace): ExprValue;
30
63
  legalChildTypes: import("../../..").TypeDesc[];
31
64
  /**
32
65
  * Some operators want to give the right hand value a chance to
@@ -45,13 +78,19 @@ export declare abstract class ExpressionDef extends MalloyElement {
45
78
  typeCheck(eNode: ExpressionDef, eVal: ExprValue): boolean;
46
79
  drillExpression(): Malloy.Expression | undefined;
47
80
  /**
48
- * This is the operation which makes partial comparison and value trees work
49
- * The default implementation merely constructs LEFT OP RIGHT, but specialized
50
- * nodes like alternation trees or or partial comparison can control how
51
- * the appplication gets generated
81
+ * This is the operation which makes partial comparison and value trees work.
82
+ * `this` is the RIGHT operand; the left arrives here as an argument. All of
83
+ * the magic of malloy expressions eventually flows through here, where an
84
+ * operator is applied to two values -- depending on the operator and the
85
+ * value types that may transform the values or even the operator.
86
+ *
87
+ * Specialized nodes like alternation trees and partial comparisons override
88
+ * this to control how the application gets generated. They are the reason
89
+ * the right operand is in charge: a partial has no bound left operand, and
90
+ * only learns it here.
52
91
  * @param fs The symbol table
53
92
  * @param op The operator being applied
54
- * @param expr The "other" (besdies 'this') value
93
+ * @param left The "other" (besides 'this') value
55
94
  * @return The translated expression
56
95
  */
57
96
  apply(fs: FieldSpace, op: BinaryMalloyOperator, left: ExpressionDef, _warnOnComplexTree?: boolean): ExprValue;
@@ -69,20 +108,8 @@ export declare class ExprDuration extends ExpressionDef {
69
108
  legalChildTypes: import("../../..").TypeDesc[];
70
109
  constructor(n: ExpressionDef, timeframe: TimestampUnit);
71
110
  apply(fs: FieldSpace, op: BinaryMalloyOperator, left: ExpressionDef): ExprValue;
72
- getExpression(fs: FieldSpace): ExprValue;
111
+ protected computeExpression(fs: FieldSpace): ExprValue;
73
112
  }
74
113
  export type MorphicType = 'date' | 'timestamp';
75
114
  export declare function getMorphicValue(mv: ExprValue, mt: MorphicType): ExprValue | undefined;
76
- /**
77
- * All of the magic of malloy expressions eventually flows to here,
78
- * where an operator is applied to two values. Depending on the
79
- * operator and value types this may involve transformations of
80
- * the values or even the operator.
81
- * @param fs FieldSpace for the symbols
82
- * @param left Left value
83
- * @param op The operator
84
- * @param right Right Value
85
- * @return ExprValue of the expression
86
- */
87
- export declare function applyBinary(fs: FieldSpace, left: ExpressionDef, op: BinaryMalloyOperator, right: ExpressionDef): ExprValue;
88
115
  export declare function checkFilterExpression(logTo: MalloyElement, ft: FilterExprType, fexpr: Expr): void;