@malloydata/malloy 0.0.100-dev231107154720 → 0.0.100

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.
@@ -7,7 +7,7 @@ import { ViewFieldReference } from '../query-items/field-references';
7
7
  import { Refinement } from '../query-properties/refinements';
8
8
  interface AppendResult {
9
9
  opList: PipeSegment[];
10
- structDef: StructDef;
10
+ structDef: () => StructDef;
11
11
  }
12
12
  /**
13
13
  * Generic abstract for all pipelines, the first segment might be a reference
@@ -24,6 +24,7 @@ export declare abstract class PipelineDesc extends MalloyElement {
24
24
  protected appendOps(pipelineOutput: FieldSpace, modelPipe: PipeSegment[]): AppendResult;
25
25
  protected refinePipeline(fs: FieldSpace, modelPipe: Pipeline): Pipeline;
26
26
  protected expandTurtle(turtleName: string, fromStruct: StructDef): {
27
+ needsExpansionDueToScalar: boolean;
27
28
  pipeline: PipeSegment[];
28
29
  location: DocumentLocation | undefined;
29
30
  annotation: Annotation | undefined;
@@ -51,15 +51,15 @@ class PipelineDesc extends malloy_element_1.MalloyElement {
51
51
  appendOps(pipelineOutput, modelPipe) {
52
52
  const returnPipe = [...modelPipe];
53
53
  const nestedIn = modelPipe.length === 0 ? this.nestedInQuerySpace : undefined;
54
- let nextFS = pipelineOutput;
54
+ let nextFS = () => pipelineOutput;
55
55
  for (const qop of this.qops) {
56
- const next = qop.getOp(nextFS, nestedIn);
56
+ const next = qop.getOp(nextFS(), nestedIn);
57
57
  returnPipe.push(next.segment);
58
- nextFS = next.outputSpace();
58
+ nextFS = () => next.outputSpace();
59
59
  }
60
60
  return {
61
61
  opList: returnPipe,
62
- structDef: nextFS.structDef(),
62
+ structDef: () => nextFS().structDef(),
63
63
  };
64
64
  }
65
65
  refinePipeline(fs, modelPipe) {
@@ -85,15 +85,35 @@ class PipelineDesc extends malloy_element_1.MalloyElement {
85
85
  this.log(`Query '${turtleName}' is not defined in source`);
86
86
  }
87
87
  else if (turtle.type !== 'turtle') {
88
- this.log(`'${turtleName}' is not a query`);
88
+ if (this.inExperiment('scalar_lenses', true)) {
89
+ return {
90
+ needsExpansionDueToScalar: true,
91
+ pipeline: [{ type: 'reduce', fields: [turtle.name] }],
92
+ location: turtle.location,
93
+ annotation,
94
+ };
95
+ }
96
+ else {
97
+ this.log(`'${turtleName}' is not a query`);
98
+ }
89
99
  }
90
100
  else {
91
101
  if (turtle.annotation) {
92
102
  annotation = { inherits: turtle.annotation };
93
103
  }
94
- return { pipeline: turtle.pipeline, location: turtle.location, annotation };
104
+ return {
105
+ pipeline: turtle.pipeline,
106
+ location: turtle.location,
107
+ annotation,
108
+ needsExpansionDueToScalar: false,
109
+ };
95
110
  }
96
- return { pipeline: [], location: undefined, annotation };
111
+ return {
112
+ pipeline: [],
113
+ location: undefined,
114
+ annotation,
115
+ needsExpansionDueToScalar: false,
116
+ };
97
117
  }
98
118
  }
99
119
  exports.PipelineDesc = PipelineDesc;
@@ -245,11 +245,13 @@ class SQAppendView extends source_query_1.SourceQueryNode {
245
245
  views.shift();
246
246
  }
247
247
  else {
248
+ // TODO implement scalar_lenses in subsequent stages
248
249
  this.sqLog(`Cannot reference view '${head}' in output of query`);
249
250
  return;
250
251
  }
251
252
  }
252
253
  else {
254
+ // TODO implement combinations for subsequent stages
253
255
  this.sqLog('query definition by combining not yet supported');
254
256
  return;
255
257
  }
@@ -23,6 +23,7 @@
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.IndexBuilder = void 0;
26
+ const malloy_types_1 = require("../../../model/malloy_types");
26
27
  const error_factory_1 = require("../error-factory");
27
28
  const filters_1 = require("../query-properties/filters");
28
29
  const indexing_1 = require("../query-properties/indexing");
@@ -63,7 +64,7 @@ class IndexBuilder {
63
64
  }
64
65
  }
65
66
  finalize(from) {
66
- if (from && from.type !== 'index') {
67
+ if (from && !(0, malloy_types_1.isIndexSegment)(from) && !(0, malloy_types_1.isPartialSegment)(from)) {
67
68
  this.resultFS.log(`Can't refine index with ${from.type}`);
68
69
  return error_factory_1.ErrorFactory.indexSegment;
69
70
  }
@@ -84,7 +85,7 @@ class IndexBuilder {
84
85
  if (this.indexOn) {
85
86
  indexSegment.weightMeasure = this.indexOn.refString;
86
87
  }
87
- if (from === null || from === void 0 ? void 0 : from.sample) {
88
+ if (from && (0, malloy_types_1.isIndexSegment)(from) && (from === null || from === void 0 ? void 0 : from.sample)) {
88
89
  indexSegment.sample = from.sample;
89
90
  }
90
91
  if (this.sample) {
@@ -0,0 +1,5 @@
1
+ import { PipeSegment } from '../../../model';
2
+ import { ReduceBuilder } from './reduce-builder';
3
+ export declare class PartialBuilder extends ReduceBuilder {
4
+ finalize(fromSeg: PipeSegment | undefined): PipeSegment;
5
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PartialBuilder = void 0;
4
+ const reduce_builder_1 = require("./reduce-builder");
5
+ class PartialBuilder extends reduce_builder_1.ReduceBuilder {
6
+ finalize(fromSeg) {
7
+ const seg = super.finalize(fromSeg);
8
+ return { ...seg, type: 'partial' };
9
+ }
10
+ }
11
+ exports.PartialBuilder = PartialBuilder;
12
+ //# sourceMappingURL=partial-builder.js.map
@@ -47,7 +47,7 @@ class ProjectBuilder extends reduce_builder_1.ReduceBuilder {
47
47
  finalize(fromSeg) {
48
48
  let from;
49
49
  if (fromSeg) {
50
- if ((0, malloy_types_1.isProjectSegment)(fromSeg)) {
50
+ if ((0, malloy_types_1.isProjectSegment)(fromSeg) || (0, malloy_types_1.isPartialSegment)(fromSeg)) {
51
51
  from = fromSeg;
52
52
  }
53
53
  else {
@@ -123,7 +123,7 @@ class ReduceBuilder {
123
123
  finalize(fromSeg) {
124
124
  let from;
125
125
  if (fromSeg) {
126
- if ((0, malloy_types_1.isReduceSegment)(fromSeg)) {
126
+ if ((0, malloy_types_1.isReduceSegment)(fromSeg) || (0, malloy_types_1.isPartialSegment)(fromSeg)) {
127
127
  from = fromSeg;
128
128
  }
129
129
  else {
@@ -76,7 +76,7 @@ class ExistingQuery extends pipeline_desc_1.PipelineDesc {
76
76
  if (head.annotation) {
77
77
  query.annotation = head.annotation;
78
78
  }
79
- return { outputStruct: appended.structDef, query };
79
+ return { outputStruct: appended.structDef(), query };
80
80
  }
81
81
  this.log(`Illegal reference to '${this.head}', query expected`);
82
82
  return oops();
@@ -28,6 +28,7 @@ const error_factory_1 = require("../error-factory");
28
28
  const static_space_1 = require("../field-space/static-space");
29
29
  const pipeline_desc_1 = require("../elements/pipeline-desc");
30
30
  const struct_utils_1 = require("../struct-utils");
31
+ const query_utils_1 = require("../query-utils");
31
32
  class FullQuery extends pipeline_desc_1.TurtleHeadedPipe {
32
33
  constructor(explore) {
33
34
  super({ explore: explore });
@@ -74,7 +75,7 @@ class FullQuery extends pipeline_desc_1.TurtleHeadedPipe {
74
75
  if (lookFor.error)
75
76
  this.log(lookFor.error);
76
77
  const name = this.turtleName.refString;
77
- const { pipeline, location, annotation } = this.expandTurtle(name, structDef);
78
+ const { pipeline, location, annotation, needsExpansionDueToScalar } = this.expandTurtle(name, structDef);
78
79
  destQuery.location = location;
79
80
  let walkPipe = pipeline;
80
81
  if (this.refinements &&
@@ -87,6 +88,12 @@ class FullQuery extends pipeline_desc_1.TurtleHeadedPipe {
87
88
  destQuery.pipeline = refined;
88
89
  walkPipe = refined;
89
90
  }
91
+ else if (needsExpansionDueToScalar) {
92
+ // When there are no refinements but the head is a scalar, we are forced
93
+ // to expand the pipeHead anyway, because the compiler doesn't support scalar
94
+ // fields as pipe heads.
95
+ destQuery.pipeline = pipeline;
96
+ }
90
97
  else {
91
98
  destQuery.pipeHead = { name };
92
99
  }
@@ -107,11 +114,17 @@ class FullQuery extends pipeline_desc_1.TurtleHeadedPipe {
107
114
  return { outputStruct: pipeOutput, query: destQuery };
108
115
  }
109
116
  else {
110
- return { outputStruct: appended.structDef, query: destQuery };
117
+ return { outputStruct: appended.structDef(), query: destQuery };
111
118
  }
112
119
  }
113
120
  query() {
114
- return this.queryComp(true).query;
121
+ const q = this.queryComp(true).query;
122
+ const { hasPartials, pipeline } = (0, query_utils_1.detectAndRemovePartialStages)(q.pipeline);
123
+ if (hasPartials) {
124
+ this.log("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
125
+ return { ...q, pipeline };
126
+ }
127
+ return q;
115
128
  }
116
129
  }
117
130
  exports.FullQuery = FullQuery;
@@ -4,5 +4,6 @@ import { LegalRefinementStage, QueryPropertyInterface } from '../types/query-pro
4
4
  export declare class Calculate extends DefinitionList<QueryItem> implements QueryPropertyInterface {
5
5
  elementType: string;
6
6
  forceQueryClass: undefined;
7
+ needsExplicitQueryClass: boolean;
7
8
  queryRefinementStage: LegalRefinementStage;
8
9
  }
@@ -30,6 +30,7 @@ class Calculate extends definition_list_1.DefinitionList {
30
30
  super(...arguments);
31
31
  this.elementType = 'calculate';
32
32
  this.forceQueryClass = undefined;
33
+ this.needsExplicitQueryClass = true;
33
34
  this.queryRefinementStage = query_property_interface_1.LegalRefinementStage.Single;
34
35
  }
35
36
  }
@@ -32,6 +32,8 @@ const query_spaces_1 = require("../field-space/query-spaces");
32
32
  const model_1 = require("../../../model");
33
33
  const field_references_1 = require("../query-items/field-references");
34
34
  const struct_utils_1 = require("../struct-utils");
35
+ const column_space_field_1 = require("../field-space/column-space-field");
36
+ const query_utils_1 = require("../query-utils");
35
37
  function isTurtle(fd) {
36
38
  const ret = fd && typeof fd !== 'string' && fd.type === 'turtle';
37
39
  return !!ret;
@@ -63,6 +65,16 @@ class TurtleDeclRoot extends pipeline_desc_1.TurtleHeadedPipe {
63
65
  reportWrongType = false;
64
66
  }
65
67
  }
68
+ else if (headEnt.found instanceof column_space_field_1.ColumnSpaceField) {
69
+ const def = headEnt.found.fieldDef();
70
+ if (this.inExperiment('scalar_lenses', true) && def.type !== 'struct') {
71
+ const newPipe = this.refinePipeline(fs, {
72
+ pipeline: [{ type: 'reduce', fields: [this.turtleName.refString] }],
73
+ });
74
+ modelPipe.pipeline = [...newPipe.pipeline];
75
+ reportWrongType = false;
76
+ }
77
+ }
66
78
  if (reportWrongType) {
67
79
  this.log(`Expected '${this.turtleName}' to be a query`);
68
80
  }
@@ -99,6 +111,11 @@ class TurtleDeclRoot extends pipeline_desc_1.TurtleHeadedPipe {
99
111
  if (this.note) {
100
112
  turtle.annotation = this.note;
101
113
  }
114
+ const { hasPartials, pipeline: cleanedPipeline } = (0, query_utils_1.detectAndRemovePartialStages)(pipe.pipeline);
115
+ if (hasPartials) {
116
+ this.log("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
117
+ return { ...turtle, pipeline: cleanedPipeline };
118
+ }
102
119
  return turtle;
103
120
  }
104
121
  makeEntry(fs) {
@@ -7,9 +7,9 @@ import { QueryClass } from '../types/query-property-interface';
7
7
  import { QueryInputSpace } from '../field-space/query-input-space';
8
8
  export declare class QOPDesc extends ListOf<QueryProperty> {
9
9
  elementType: string;
10
- opClass: QueryClass;
10
+ opClass: QueryClass | undefined;
11
11
  private refineThis?;
12
- protected computeType(): QueryClass;
12
+ protected computeType(): QueryClass | undefined;
13
13
  refineFrom(existing: PipeSegment): void;
14
14
  private getBuilder;
15
15
  getOp(inputFS: FieldSpace, headFieldSpace: QueryInputSpace | undefined): OpDesc;
@@ -30,41 +30,44 @@ const malloy_element_1 = require("../types/malloy-element");
30
30
  const struct_utils_1 = require("../struct-utils");
31
31
  const static_space_1 = require("../field-space/static-space");
32
32
  const query_property_interface_1 = require("../types/query-property-interface");
33
+ const partial_builder_1 = require("../query-builders/partial-builder");
33
34
  class QOPDesc extends malloy_element_1.ListOf {
34
35
  constructor() {
35
36
  super(...arguments);
36
37
  this.elementType = 'queryOperation';
37
- this.opClass = query_property_interface_1.QueryClass.Grouping;
38
38
  }
39
39
  computeType() {
40
- let mustBe;
40
+ var _a;
41
+ let guessType;
42
+ let needsExplicitQueryClass = false;
41
43
  if (this.refineThis) {
42
44
  if (this.refineThis.type === 'reduce') {
43
- mustBe = query_property_interface_1.QueryClass.Grouping;
45
+ guessType = query_property_interface_1.QueryClass.Grouping;
44
46
  }
45
47
  else if (this.refineThis.type === 'project') {
46
- mustBe = query_property_interface_1.QueryClass.Project;
48
+ guessType = query_property_interface_1.QueryClass.Project;
47
49
  }
48
- else {
49
- mustBe = query_property_interface_1.QueryClass.Index;
50
+ else if (this.refineThis.type === 'index') {
51
+ guessType = query_property_interface_1.QueryClass.Index;
50
52
  }
51
53
  }
52
54
  for (const el of this.list) {
53
55
  if (el.forceQueryClass) {
54
- if (mustBe) {
55
- if (mustBe !== el.forceQueryClass) {
56
- el.log(`Not legal in ${mustBe} query`);
56
+ if (guessType) {
57
+ if (guessType !== el.forceQueryClass) {
58
+ el.log(`Not legal in ${guessType} query`);
57
59
  }
58
60
  }
59
61
  else {
60
- mustBe = el.forceQueryClass;
62
+ guessType = el.forceQueryClass;
61
63
  }
62
64
  }
65
+ needsExplicitQueryClass || (needsExplicitQueryClass = (_a = el.needsExplicitQueryClass) !== null && _a !== void 0 ? _a : false);
63
66
  }
64
- if (mustBe === undefined) {
65
- this.log("Can't determine query type (group_by/aggregate/nest,project,index)");
67
+ if (guessType === undefined && needsExplicitQueryClass) {
68
+ this.log("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
69
+ guessType = query_property_interface_1.QueryClass.Project;
66
70
  }
67
- const guessType = mustBe || query_property_interface_1.QueryClass.Grouping;
68
71
  this.opClass = guessType;
69
72
  return guessType;
70
73
  }
@@ -79,6 +82,8 @@ class QOPDesc extends malloy_element_1.ListOf {
79
82
  return new project_builder_1.ProjectBuilder(baseFS, this.refineThis);
80
83
  case query_property_interface_1.QueryClass.Index:
81
84
  return new index_builder_1.IndexBuilder(baseFS, this.refineThis);
85
+ case undefined:
86
+ return new partial_builder_1.PartialBuilder(baseFS, this.refineThis);
82
87
  }
83
88
  }
84
89
  getOp(inputFS, headFieldSpace) {
@@ -58,6 +58,11 @@ class NamedRefinement extends Refinement {
58
58
  }
59
59
  return fieldDef.pipeline[0];
60
60
  }
61
+ if ((fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.type) !== 'struct') {
62
+ if (this.name.inExperiment('scalar_lenses', true)) {
63
+ return { type: 'reduce', fields: [this.name.refString] };
64
+ }
65
+ }
61
66
  }
62
67
  this.name.log(`named refinement \`${this.name.refString}\` must be a view, found a ${res.found.describeType().dataType}`);
63
68
  }
@@ -76,7 +81,11 @@ class NamedRefinement extends Refinement {
76
81
  const to = { ..._to };
77
82
  const from = this.getRefinementSegment(inputFS);
78
83
  if (from) {
79
- if (from.type !== to.type) {
84
+ // TODO need to disallow partial + index for now to make the types happy
85
+ if (to.type === 'partial' && from.type !== 'index') {
86
+ to.type = from.type;
87
+ }
88
+ else if (from.type !== to.type) {
80
89
  this.log(`cannot refine ${to.type} view with ${from.type} view`);
81
90
  }
82
91
  if (from.type !== 'index' && to.type !== 'index') {
@@ -0,0 +1,5 @@
1
+ import { PipeSegment } from '../../model';
2
+ export declare function detectAndRemovePartialStages(pipeline: PipeSegment[]): {
3
+ hasPartials: boolean;
4
+ pipeline: PipeSegment[];
5
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files
7
+ * (the "Software"), to deal in the Software without restriction,
8
+ * including without limitation the rights to use, copy, modify, merge,
9
+ * publish, distribute, sublicense, and/or sell copies of the Software,
10
+ * and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.detectAndRemovePartialStages = void 0;
26
+ const model_1 = require("../../model");
27
+ // We don't want to ever generate actual 'partial' stages, so convert this
28
+ // into a reduce so the compiler doesn't explode
29
+ function detectAndRemovePartialStages(pipeline) {
30
+ const cleaned = [];
31
+ let hasPartials = false;
32
+ for (const segment of pipeline) {
33
+ if ((0, model_1.isPartialSegment)(segment)) {
34
+ cleaned.push({ ...segment, type: 'reduce' });
35
+ hasPartials = true;
36
+ }
37
+ else {
38
+ cleaned.push(segment);
39
+ }
40
+ }
41
+ return { hasPartials, pipeline: cleaned };
42
+ }
43
+ exports.detectAndRemovePartialStages = detectAndRemovePartialStages;
44
+ //# sourceMappingURL=query-utils.js.map
@@ -25,9 +25,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.getStructFieldDef = exports.getFinalStruct = exports.opOutputStruct = void 0;
26
26
  const util_1 = require("util");
27
27
  const malloy_query_1 = require("../../model/malloy_query");
28
+ const malloy_types_1 = require("../../model/malloy_types");
28
29
  const error_factory_1 = require("./error-factory");
29
30
  function opOutputStruct(logTo, inputStruct, opDesc) {
30
31
  const badModel = error_factory_1.ErrorFactory.isErrorStructDef(inputStruct);
32
+ // We don't want to expose partial segments to the compiler
33
+ if ((0, malloy_types_1.isPartialSegment)(opDesc)) {
34
+ opDesc = { ...opDesc, type: 'reduce' };
35
+ }
31
36
  // Don't call into the model code with a broken model
32
37
  if (!badModel) {
33
38
  try {
@@ -19,5 +19,6 @@ export declare enum LegalRefinementStage {
19
19
  export interface QueryPropertyInterface {
20
20
  queryRefinementStage: LegalRefinementStage | undefined;
21
21
  forceQueryClass: QueryClass | undefined;
22
+ needsExplicitQueryClass?: boolean;
22
23
  queryExecute?: (executeFor: QueryBuilder) => void;
23
24
  }
@@ -95,6 +95,15 @@ describe('lenses', () => {
95
95
  run: x -> idx + proj
96
96
  `).translationToFailWith('cannot refine reduce view with project view', 'cannot refine reduce view with index view', 'cannot refine project view with index view', 'cannot refine project view with reduce view', 'cannot refine index view with reduce view', 'cannot refine index view with project view');
97
97
  });
98
+ test('cannot reference dimension at head of query ', () => {
99
+ expect((0, test_translator_1.markSource) `
100
+ source: x is a extend {
101
+ dimension: n is 1
102
+ view: d is { group_by: n1 is 1 }
103
+ }
104
+ run: x -> n + d
105
+ `).translationToFailWith("'n' is not a query");
106
+ });
98
107
  test('cannot reference dimension', () => {
99
108
  expect((0, test_translator_1.markSource) `
100
109
  source: x is a extend {
@@ -104,6 +113,45 @@ describe('lenses', () => {
104
113
  run: x -> d + n
105
114
  `).translationToFailWith('named refinement `n` must be a view, found a number');
106
115
  });
116
+ test('can reference dimension at head of query when experiment is enabled', () => {
117
+ expect((0, test_translator_1.markSource) `
118
+ ##! experimental { scalar_lenses }
119
+ source: x is a extend {
120
+ dimension: n is 1
121
+ }
122
+ run: x -> n
123
+ `).toTranslate();
124
+ });
125
+ test('can reference dimension in refinement when experiment is enabled', () => {
126
+ expect((0, test_translator_1.markSource) `
127
+ ##! experimental { scalar_lenses }
128
+ source: x is a extend {
129
+ dimension: n is 1
130
+ view: d is { group_by: n1 is 1 }
131
+ }
132
+ run: x -> d + n
133
+ `).toTranslate();
134
+ });
135
+ test('cannot nest dimension even when experiment is enabled', () => {
136
+ expect((0, test_translator_1.markSource) `
137
+ ##! experimental { scalar_lenses }
138
+ source: x is a extend {
139
+ dimension: n is 1
140
+ view: d is { nest: n }
141
+ }
142
+ run: x -> d
143
+ `).translationToFailWith('Cannot use a scalar field in a nest operation, did you mean to use a group_by or select operation instead?');
144
+ });
145
+ test('can nest dimension with refinement when experiment is enabled', () => {
146
+ expect((0, test_translator_1.markSource) `
147
+ ##! experimental { scalar_lenses }
148
+ source: x is a extend {
149
+ dimension: n is 1
150
+ view: d is { nest: n + { where: n > 0 } }
151
+ }
152
+ run: x -> d
153
+ `).toTranslate();
154
+ });
107
155
  test('cannot reference join', () => {
108
156
  expect((0, test_translator_1.markSource) `
109
157
  source: x is a extend {
@@ -127,4 +175,41 @@ describe('lenses', () => {
127
175
  `).translationToFailWith('Named refinements of multi-stage views are not supported', 'named refinement `multi` must have exactly one stage');
128
176
  });
129
177
  });
178
+ describe('partial views', () => {
179
+ test('allow where-headed refinement chains', () => {
180
+ expect((0, test_translator_1.markSource) `
181
+ source: x is a extend {
182
+ view: metrics is { aggregate: c is count() }
183
+ view: cool_metrics is { where: true } + metrics
184
+ }
185
+ `).toTranslate();
186
+ });
187
+ test.skip('partial with index', () => {
188
+ expect((0, test_translator_1.markSource) `
189
+ source: x is a extend {
190
+ view: idx is { index: * }
191
+ }
192
+ run: x -> { where: true } + idx
193
+ `).toTranslate();
194
+ });
195
+ test('disallow chains that have no fields in view', () => {
196
+ expect((0, test_translator_1.markSource) `
197
+ source: x is a extend {
198
+ view: bad1 is { where: true }
199
+ view: bad2 is { where: true } + { where: false }
200
+ }
201
+ `).translationToFailWith("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
202
+ });
203
+ test('disallow chains that have no fields in multi-stage', () => {
204
+ expect((0, test_translator_1.markSource) `
205
+ source: x is a extend {
206
+ view: v is { group_by: ai }
207
+ view: v2 is v -> { where: true }
208
+ view: v3 is { where: true } -> { group_by: undef }
209
+ }
210
+ run: x -> v -> { where: true }
211
+ run: x -> { where: true } -> { group_by: undef }
212
+ `).translationToFailWith("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "'undef' is not defined", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)", "'undef' is not defined", "Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
213
+ });
214
+ });
130
215
  //# sourceMappingURL=lenses.spec.js.map
@@ -117,7 +117,7 @@ describe('error handling', () => {
117
117
  expect('\n').toTranslate();
118
118
  });
119
119
  test('query without fields', () => {
120
- expect('run: a -> { top: 5 }').translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
120
+ expect('run: a -> { top: 5 }').translationToFailWith("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
121
121
  });
122
122
  test('refine cannot change query type', () => {
123
123
  expect('run: ab -> aturtle + { select: astr }').translationToFailWith(/Not legal in grouping query/);
@@ -515,7 +515,7 @@ describe('query:', () => {
515
515
  test('cannot use calculate with no other fields', () => {
516
516
  expect(`run: a -> {
517
517
  calculate: x is row_number()
518
- }`).translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
518
+ }`).translationToFailWith("Can't determine view type (`group_by` / `aggregate` / `nest`, `project`, `index`)");
519
519
  });
520
520
  // TODO someday make it so we can order by an analytic function
521
521
  test('today: cannot order by analytic function', () => {
@@ -1320,6 +1320,8 @@ class QueryQuery extends QueryField {
1320
1320
  return new QueryQueryProject(flatTurtleDef, parent, stageWriter);
1321
1321
  case 'index':
1322
1322
  return new QueryQueryIndex(flatTurtleDef, parent, stageWriter);
1323
+ case 'partial':
1324
+ throw new Error('Attempt to make query out of partial stage');
1323
1325
  }
1324
1326
  }
1325
1327
  inNestedPipeline() {
@@ -415,6 +415,10 @@ export interface ReduceSegment extends QuerySegment {
415
415
  type: 'reduce';
416
416
  }
417
417
  export declare function isReduceSegment(pe: PipeSegment): pe is ReduceSegment;
418
+ export interface PartialSegment extends QuerySegment {
419
+ type: 'partial';
420
+ }
421
+ export declare function isPartialSegment(pe: PipeSegment): pe is PartialSegment;
418
422
  export interface ProjectSegment extends QuerySegment {
419
423
  type: 'project';
420
424
  }
@@ -442,7 +446,7 @@ export interface IndexSegment extends Filtered {
442
446
  }
443
447
  export declare function isIndexSegment(pe: PipeSegment): pe is IndexSegment;
444
448
  export interface QuerySegment extends Filtered {
445
- type: 'reduce' | 'project';
449
+ type: 'reduce' | 'project' | 'partial';
446
450
  fields: QueryFieldDef[];
447
451
  extendSource?: FieldDef[];
448
452
  limit?: number;
@@ -22,8 +22,8 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.mergeEvalSpaces = exports.isSQLFragment = exports.isJoinOn = exports.isIndexSegment = exports.isSamplingEnable = exports.isSamplingPercent = exports.isSamplingRows = exports.isQuerySegment = exports.isProjectSegment = exports.isReduceSegment = exports.refIsStructDef = exports.isByExpression = exports.isByName = exports.ValueType = exports.isExtractUnit = exports.isTimestampUnit = exports.isDateUnit = exports.FieldIsIntrinsic = exports.isCastType = exports.isAtomicFieldType = exports.isTimeFieldType = exports.hasExpression = exports.maxOfExpressionTypes = exports.maxExpressionType = exports.isExpressionTypeLEQ = exports.expressionIsAnalytic = exports.expressionIsCalculation = exports.expressionInvolvesAggregate = exports.expressionIsUngroupedAggregate = exports.expressionIsAggregate = exports.expressionIsScalar = exports.mkExpr = exports.isApplyFragment = exports.isApplyValue = exports.isParameterFragment = exports.isFieldFragment = exports.isSpreadFragment = exports.isSQLExpressionFragment = exports.isFunctionCallFragment = exports.isFunctionParameterFragment = exports.isUngroupFragment = exports.isAsymmetricFragment = exports.isAggregateFragment = exports.isDialectFragment = exports.isFilterFragment = exports.isOutputFieldFragment = exports.isFilteredAliasedName = exports.paramHasValue = exports.isConditionParameter = exports.isValueParameter = void 0;
26
- exports.isValueDate = exports.isValueTimestamp = exports.isValueBoolean = exports.isValueNumber = exports.isValueString = exports.isMeasureLike = exports.getPhysicalFields = exports.getDimensions = exports.isPhysical = exports.isDimensional = exports.isTurtleDef = exports.getIdentifier = exports.isFieldStructDef = exports.isFieldTimeBased = exports.isFieldTypeDef = exports.isSQLBlockStruct = void 0;
25
+ exports.isSQLFragment = exports.isJoinOn = exports.isIndexSegment = exports.isSamplingEnable = exports.isSamplingPercent = exports.isSamplingRows = exports.isQuerySegment = exports.isProjectSegment = exports.isPartialSegment = exports.isReduceSegment = exports.refIsStructDef = exports.isByExpression = exports.isByName = exports.ValueType = exports.isExtractUnit = exports.isTimestampUnit = exports.isDateUnit = exports.FieldIsIntrinsic = exports.isCastType = exports.isAtomicFieldType = exports.isTimeFieldType = exports.hasExpression = exports.maxOfExpressionTypes = exports.maxExpressionType = exports.isExpressionTypeLEQ = exports.expressionIsAnalytic = exports.expressionIsCalculation = exports.expressionInvolvesAggregate = exports.expressionIsUngroupedAggregate = exports.expressionIsAggregate = exports.expressionIsScalar = exports.mkExpr = exports.isApplyFragment = exports.isApplyValue = exports.isParameterFragment = exports.isFieldFragment = exports.isSpreadFragment = exports.isSQLExpressionFragment = exports.isFunctionCallFragment = exports.isFunctionParameterFragment = exports.isUngroupFragment = exports.isAsymmetricFragment = exports.isAggregateFragment = exports.isDialectFragment = exports.isFilterFragment = exports.isOutputFieldFragment = exports.isFilteredAliasedName = exports.paramHasValue = exports.isConditionParameter = exports.isValueParameter = void 0;
26
+ exports.isValueDate = exports.isValueTimestamp = exports.isValueBoolean = exports.isValueNumber = exports.isValueString = exports.isMeasureLike = exports.getPhysicalFields = exports.getDimensions = exports.isPhysical = exports.isDimensional = exports.isTurtleDef = exports.getIdentifier = exports.isFieldStructDef = exports.isFieldTimeBased = exports.isFieldTypeDef = exports.isSQLBlockStruct = exports.mergeEvalSpaces = void 0;
27
27
  function isValueParameter(p) {
28
28
  return p.value !== undefined;
29
29
  }
@@ -286,6 +286,10 @@ function isReduceSegment(pe) {
286
286
  return pe.type === 'reduce';
287
287
  }
288
288
  exports.isReduceSegment = isReduceSegment;
289
+ function isPartialSegment(pe) {
290
+ return pe.type === 'partial';
291
+ }
292
+ exports.isPartialSegment = isPartialSegment;
289
293
  function isProjectSegment(pe) {
290
294
  return pe.type === 'project';
291
295
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.100-dev231107154720",
3
+ "version": "0.0.100",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",