@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.
- package/dist/lang/ast/elements/pipeline-desc.d.ts +2 -1
- package/dist/lang/ast/elements/pipeline-desc.js +27 -7
- package/dist/lang/ast/elements/source-query-nodes.js +2 -0
- package/dist/lang/ast/query-builders/index-builder.js +3 -2
- package/dist/lang/ast/query-builders/partial-builder.d.ts +5 -0
- package/dist/lang/ast/query-builders/partial-builder.js +12 -0
- package/dist/lang/ast/query-builders/project-builder.js +1 -1
- package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
- package/dist/lang/ast/query-elements/existing-query.js +1 -1
- package/dist/lang/ast/query-elements/full-query.js +16 -3
- package/dist/lang/ast/query-properties/calculate.d.ts +1 -0
- package/dist/lang/ast/query-properties/calculate.js +1 -0
- package/dist/lang/ast/query-properties/nest.js +17 -0
- package/dist/lang/ast/query-properties/qop-desc.d.ts +2 -2
- package/dist/lang/ast/query-properties/qop-desc.js +18 -13
- package/dist/lang/ast/query-properties/refinements.js +10 -1
- package/dist/lang/ast/query-utils.d.ts +5 -0
- package/dist/lang/ast/query-utils.js +44 -0
- package/dist/lang/ast/struct-utils.js +5 -0
- package/dist/lang/ast/types/query-property-interface.d.ts +1 -0
- package/dist/lang/test/lenses.spec.js +85 -0
- package/dist/lang/test/parse.spec.js +1 -1
- package/dist/lang/test/query.spec.js +1 -1
- package/dist/model/malloy_query.js +2 -0
- package/dist/model/malloy_types.d.ts +5 -1
- package/dist/model/malloy_types.js +6 -2
- package/package.json +1 -1
|
@@ -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.
|
|
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 {
|
|
104
|
+
return {
|
|
105
|
+
pipeline: turtle.pipeline,
|
|
106
|
+
location: turtle.location,
|
|
107
|
+
annotation,
|
|
108
|
+
needsExpansionDueToScalar: false,
|
|
109
|
+
};
|
|
95
110
|
}
|
|
96
|
-
return {
|
|
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
|
|
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,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
|
-
|
|
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
|
-
|
|
40
|
+
var _a;
|
|
41
|
+
let guessType;
|
|
42
|
+
let needsExplicitQueryClass = false;
|
|
41
43
|
if (this.refineThis) {
|
|
42
44
|
if (this.refineThis.type === 'reduce') {
|
|
43
|
-
|
|
45
|
+
guessType = query_property_interface_1.QueryClass.Grouping;
|
|
44
46
|
}
|
|
45
47
|
else if (this.refineThis.type === 'project') {
|
|
46
|
-
|
|
48
|
+
guessType = query_property_interface_1.QueryClass.Project;
|
|
47
49
|
}
|
|
48
|
-
else {
|
|
49
|
-
|
|
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 (
|
|
55
|
-
if (
|
|
56
|
-
el.log(`Not legal in ${
|
|
56
|
+
if (guessType) {
|
|
57
|
+
if (guessType !== el.forceQueryClass) {
|
|
58
|
+
el.log(`Not legal in ${guessType} query`);
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
else {
|
|
60
|
-
|
|
62
|
+
guessType = el.forceQueryClass;
|
|
61
63
|
}
|
|
62
64
|
}
|
|
65
|
+
needsExplicitQueryClass || (needsExplicitQueryClass = (_a = el.needsExplicitQueryClass) !== null && _a !== void 0 ? _a : false);
|
|
63
66
|
}
|
|
64
|
-
if (
|
|
65
|
-
this.log("Can't determine
|
|
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
|
-
|
|
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,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
|
|
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
|
|
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.
|
|
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
|
}
|