@malloydata/malloy 0.0.97-dev231026184251 → 0.0.97
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 +4 -4
- package/dist/lang/ast/elements/pipeline-desc.js +10 -54
- package/dist/lang/ast/elements/source-query-nodes.d.ts +2 -2
- package/dist/lang/ast/elements/source-query-nodes.js +7 -26
- package/dist/lang/ast/query-elements/existing-query.js +2 -1
- package/dist/lang/ast/query-elements/full-query.js +16 -3
- package/dist/lang/ast/query-items/field-references.js +1 -3
- package/dist/lang/ast/query-properties/nest.js +11 -4
- package/dist/lang/ast/query-properties/refinements.d.ts +25 -0
- package/dist/lang/ast/query-properties/refinements.js +186 -0
- package/dist/lang/ast/struct-utils.d.ts +1 -0
- package/dist/lang/ast/struct-utils.js +8 -1
- package/dist/lang/ast/types/malloy-element.d.ts +1 -1
- package/dist/lang/ast/types/malloy-element.js +4 -2
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +125 -111
- package/dist/lang/lib/Malloy/MalloyParser.js +1796 -1656
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +11 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +7 -0
- package/dist/lang/malloy-to-ast.d.ts +1 -1
- package/dist/lang/malloy-to-ast.js +24 -16
- package/dist/lang/test/lenses.spec.d.ts +1 -0
- package/dist/lang/test/lenses.spec.js +130 -0
- package/package.json +1 -1
|
@@ -3,6 +3,8 @@ import { FieldName, FieldSpace } from '../types/field-space';
|
|
|
3
3
|
import { MalloyElement } from '../types/malloy-element';
|
|
4
4
|
import { QOPDesc } from '../query-properties/qop-desc';
|
|
5
5
|
import { QueryInputSpace } from '../field-space/query-input-space';
|
|
6
|
+
import { ViewFieldReference } from '../query-items/field-references';
|
|
7
|
+
import { Refinement } from '../query-properties/refinements';
|
|
6
8
|
interface AppendResult {
|
|
7
9
|
opList: PipeSegment[];
|
|
8
10
|
structDef: StructDef;
|
|
@@ -13,21 +15,19 @@ interface AppendResult {
|
|
|
13
15
|
* is refers to the first segment of the composed pipeline.
|
|
14
16
|
*/
|
|
15
17
|
export declare abstract class PipelineDesc extends MalloyElement {
|
|
16
|
-
protected
|
|
18
|
+
protected refinements?: Refinement[];
|
|
17
19
|
protected qops: QOPDesc[];
|
|
18
20
|
nestedInQuerySpace?: QueryInputSpace;
|
|
19
21
|
alreadyRefined(): boolean;
|
|
20
|
-
refineWith(
|
|
22
|
+
refineWith(refinements: (QOPDesc | ViewFieldReference)[]): void;
|
|
21
23
|
addSegments(...segDesc: QOPDesc[]): void;
|
|
22
24
|
protected appendOps(pipelineOutput: FieldSpace, modelPipe: PipeSegment[]): AppendResult;
|
|
23
|
-
private refineSegment;
|
|
24
25
|
protected refinePipeline(fs: FieldSpace, modelPipe: Pipeline): Pipeline;
|
|
25
26
|
protected expandTurtle(turtleName: string, fromStruct: StructDef): {
|
|
26
27
|
pipeline: PipeSegment[];
|
|
27
28
|
location: DocumentLocation | undefined;
|
|
28
29
|
annotation: Annotation | undefined;
|
|
29
30
|
};
|
|
30
|
-
protected getFinalStruct(walkStruct: StructDef, pipeline: PipeSegment[]): StructDef;
|
|
31
31
|
}
|
|
32
32
|
export declare abstract class TurtleHeadedPipe extends PipelineDesc {
|
|
33
33
|
_turtleName?: FieldName;
|
|
@@ -24,10 +24,8 @@
|
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.TurtleHeadedPipe = exports.PipelineDesc = void 0;
|
|
26
26
|
const malloy_element_1 = require("../types/malloy-element");
|
|
27
|
-
const qop_desc_1 = require("../query-properties/qop-desc");
|
|
28
27
|
const struct_utils_1 = require("../struct-utils");
|
|
29
|
-
const
|
|
30
|
-
const static_space_1 = require("../field-space/static-space");
|
|
28
|
+
const refinements_1 = require("../query-properties/refinements");
|
|
31
29
|
/**
|
|
32
30
|
* Generic abstract for all pipelines, the first segment might be a reference
|
|
33
31
|
* to an existing pipeline (query or turtle), and if there is a refinement it
|
|
@@ -39,11 +37,12 @@ class PipelineDesc extends malloy_element_1.MalloyElement {
|
|
|
39
37
|
this.qops = [];
|
|
40
38
|
}
|
|
41
39
|
alreadyRefined() {
|
|
42
|
-
return this.
|
|
40
|
+
return this.refinements !== undefined;
|
|
43
41
|
}
|
|
44
|
-
refineWith(
|
|
45
|
-
|
|
46
|
-
this.
|
|
42
|
+
refineWith(refinements) {
|
|
43
|
+
const ref = refinements.map(refinement => refinements_1.Refinement.from(refinement));
|
|
44
|
+
this.refinements = ref;
|
|
45
|
+
this.has({ refinements: ref });
|
|
47
46
|
}
|
|
48
47
|
addSegments(...segDesc) {
|
|
49
48
|
this.qops.push(...segDesc);
|
|
@@ -63,12 +62,8 @@ class PipelineDesc extends malloy_element_1.MalloyElement {
|
|
|
63
62
|
structDef: nextFS.structDef(),
|
|
64
63
|
};
|
|
65
64
|
}
|
|
66
|
-
refineSegment(fs, refinement, seg) {
|
|
67
|
-
refinement.refineFrom(seg);
|
|
68
|
-
return refinement.getOp(fs, this.nestedInQuerySpace).segment;
|
|
69
|
-
}
|
|
70
65
|
refinePipeline(fs, modelPipe) {
|
|
71
|
-
if (!this.
|
|
66
|
+
if (!this.refinements) {
|
|
72
67
|
return modelPipe;
|
|
73
68
|
}
|
|
74
69
|
let pipeline = [];
|
|
@@ -77,43 +72,10 @@ class PipelineDesc extends malloy_element_1.MalloyElement {
|
|
|
77
72
|
pipeline.push(...turtlePipe);
|
|
78
73
|
}
|
|
79
74
|
pipeline.push(...modelPipe.pipeline);
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
// I could let everything fall through to the head/tail case, it should be the
|
|
83
|
-
// same, but this does less computation and is the most common case.
|
|
84
|
-
pipeline = [this.refineSegment(fs, this.withRefinement, firstSeg)];
|
|
85
|
-
this.withRefinement = undefined;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
const headRefinements = new qop_desc_1.QOPDesc([]);
|
|
89
|
-
const tailRefinements = new qop_desc_1.QOPDesc([]);
|
|
90
|
-
for (const qop of this.withRefinement.list) {
|
|
91
|
-
switch (qop.queryRefinementStage) {
|
|
92
|
-
case query_property_interface_1.LegalRefinementStage.Head:
|
|
93
|
-
headRefinements.push(qop);
|
|
94
|
-
break;
|
|
95
|
-
case query_property_interface_1.LegalRefinementStage.Single:
|
|
96
|
-
qop.log('Illegal in refinement of a query with more than one stage');
|
|
97
|
-
break;
|
|
98
|
-
case query_property_interface_1.LegalRefinementStage.Tail:
|
|
99
|
-
tailRefinements.push(qop);
|
|
100
|
-
break;
|
|
101
|
-
default:
|
|
102
|
-
qop.log('Illegal query refinement');
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (headRefinements.notEmpty()) {
|
|
106
|
-
this.has({ headRefinements });
|
|
107
|
-
pipeline[0] = this.refineSegment(fs, headRefinements, firstSeg);
|
|
108
|
-
}
|
|
109
|
-
if (tailRefinements.notEmpty()) {
|
|
110
|
-
const last = pipeline.length - 1;
|
|
111
|
-
this.has({ tailRefinements });
|
|
112
|
-
const finalIn = this.getFinalStruct(fs.structDef(), pipeline.slice(-1));
|
|
113
|
-
pipeline[last] = this.refineSegment(new static_space_1.StaticSpace(finalIn), tailRefinements, pipeline[last]);
|
|
114
|
-
}
|
|
75
|
+
for (const refinement of this.refinements) {
|
|
76
|
+
pipeline = refinement.refine(fs, pipeline);
|
|
115
77
|
}
|
|
116
|
-
this.
|
|
78
|
+
this.refinements = undefined;
|
|
117
79
|
return { pipeline };
|
|
118
80
|
}
|
|
119
81
|
expandTurtle(turtleName, fromStruct) {
|
|
@@ -133,12 +95,6 @@ class PipelineDesc extends malloy_element_1.MalloyElement {
|
|
|
133
95
|
}
|
|
134
96
|
return { pipeline: [], location: undefined, annotation };
|
|
135
97
|
}
|
|
136
|
-
getFinalStruct(walkStruct, pipeline) {
|
|
137
|
-
for (const modelQop of pipeline) {
|
|
138
|
-
walkStruct = (0, struct_utils_1.opOutputStruct)(this, walkStruct, modelQop);
|
|
139
|
-
}
|
|
140
|
-
return walkStruct;
|
|
141
|
-
}
|
|
142
98
|
}
|
|
143
99
|
exports.PipelineDesc = PipelineDesc;
|
|
144
100
|
class TurtleHeadedPipe extends PipelineDesc {
|
|
@@ -49,9 +49,9 @@ export declare class SQAppendView extends SourceQueryNode {
|
|
|
49
49
|
}
|
|
50
50
|
export declare class SQRefinedQuery extends SourceQueryNode {
|
|
51
51
|
readonly toRefine: SourceQueryNode;
|
|
52
|
-
readonly refine: QOPDesc;
|
|
52
|
+
readonly refine: QOPDesc | ViewFieldReference;
|
|
53
53
|
elementType: string;
|
|
54
|
-
constructor(toRefine: SourceQueryNode, refine: QOPDesc);
|
|
54
|
+
constructor(toRefine: SourceQueryNode, refine: QOPDesc | ViewFieldReference);
|
|
55
55
|
getQuery(): QueryElement | undefined;
|
|
56
56
|
getSource(): QuerySource | undefined;
|
|
57
57
|
}
|
|
@@ -118,7 +118,7 @@ class SQLegacyModify extends source_query_1.SourceQueryNode {
|
|
|
118
118
|
stmt.log('Unable to add this refinement to query');
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
asQuery.refineWith(new qop_desc_1.QOPDesc(stmts));
|
|
121
|
+
asQuery.refineWith([new qop_desc_1.QOPDesc(stmts)]);
|
|
122
122
|
this.has({ asQuery });
|
|
123
123
|
return asQuery;
|
|
124
124
|
}
|
|
@@ -219,34 +219,15 @@ class SQAppendView extends source_query_1.SourceQueryNode {
|
|
|
219
219
|
theQuery = new full_query_1.FullQuery(querySrc);
|
|
220
220
|
this.has({ theQuery });
|
|
221
221
|
const head = views[0];
|
|
222
|
+
const refinements = views.slice(1);
|
|
222
223
|
if (head instanceof field_references_1.ViewFieldReference) {
|
|
223
224
|
theQuery.turtleName = head.list[0];
|
|
224
|
-
if (views.length === 1) {
|
|
225
|
-
return theQuery;
|
|
226
|
-
}
|
|
227
|
-
if (views.length === 2) {
|
|
228
|
-
if (views[1] instanceof qop_desc_1.QOPDesc) {
|
|
229
|
-
theQuery.refineWith(views[1]);
|
|
230
|
-
return theQuery;
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
this.sqLog('Cannot refine with a named view');
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
this.sqLog('Cannot have multiple refinements');
|
|
239
|
-
}
|
|
240
225
|
}
|
|
241
|
-
else if (
|
|
242
|
-
|
|
243
|
-
theQuery.addSegments(head);
|
|
244
|
-
return theQuery;
|
|
245
|
-
}
|
|
226
|
+
else if (head instanceof qop_desc_1.QOPDesc) {
|
|
227
|
+
theQuery.addSegments(head);
|
|
246
228
|
}
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
return;
|
|
229
|
+
if (refinements) {
|
|
230
|
+
theQuery.refineWith(refinements);
|
|
250
231
|
}
|
|
251
232
|
return theQuery;
|
|
252
233
|
}
|
|
@@ -309,7 +290,7 @@ class SQRefinedQuery extends source_query_1.SourceQueryNode {
|
|
|
309
290
|
this.sqLog('Cannot refine a refined query');
|
|
310
291
|
return;
|
|
311
292
|
}
|
|
312
|
-
refinedQuery.refineWith(this.refine);
|
|
293
|
+
refinedQuery.refineWith([this.refine]);
|
|
313
294
|
this.has({ query: refinedQuery });
|
|
314
295
|
return refinedQuery;
|
|
315
296
|
}
|
|
@@ -26,6 +26,7 @@ exports.ExistingQuery = void 0;
|
|
|
26
26
|
const pipeline_desc_1 = require("../elements/pipeline-desc");
|
|
27
27
|
const error_factory_1 = require("../error-factory");
|
|
28
28
|
const static_space_1 = require("../field-space/static-space");
|
|
29
|
+
const struct_utils_1 = require("../struct-utils");
|
|
29
30
|
const query_head_struct_1 = require("./query-head-struct");
|
|
30
31
|
class ExistingQuery extends pipeline_desc_1.PipelineDesc {
|
|
31
32
|
constructor() {
|
|
@@ -63,7 +64,7 @@ class ExistingQuery extends pipeline_desc_1.PipelineDesc {
|
|
|
63
64
|
const exploreStruct = queryHead.structDef();
|
|
64
65
|
const exploreFS = new static_space_1.StaticSpace(exploreStruct);
|
|
65
66
|
const sourcePipe = this.refinePipeline(exploreFS, head);
|
|
66
|
-
const walkStruct =
|
|
67
|
+
const walkStruct = (0, struct_utils_1.getFinalStruct)(this, exploreStruct, sourcePipe.pipeline);
|
|
67
68
|
const appended = this.appendOps(new static_space_1.StaticSpace(walkStruct), sourcePipe.pipeline);
|
|
68
69
|
const destPipe = { ...sourcePipe, pipeline: appended.opList };
|
|
69
70
|
const query = {
|
|
@@ -27,6 +27,7 @@ const malloy_types_1 = require("../../../model/malloy_types");
|
|
|
27
27
|
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
|
+
const struct_utils_1 = require("../struct-utils");
|
|
30
31
|
class FullQuery extends pipeline_desc_1.TurtleHeadedPipe {
|
|
31
32
|
constructor(explore) {
|
|
32
33
|
super({ explore: explore });
|
|
@@ -76,7 +77,9 @@ class FullQuery extends pipeline_desc_1.TurtleHeadedPipe {
|
|
|
76
77
|
const { pipeline, location, annotation } = this.expandTurtle(name, structDef);
|
|
77
78
|
destQuery.location = location;
|
|
78
79
|
let walkPipe = pipeline;
|
|
79
|
-
if (this.
|
|
80
|
+
if (this.refinements &&
|
|
81
|
+
this.refinements.length > 0 &&
|
|
82
|
+
pipeline.length !== 0) {
|
|
80
83
|
const refined = this.refinePipeline(pipeFS, { pipeline }).pipeline;
|
|
81
84
|
// TODO there is an issue with losing the name of the turtle
|
|
82
85
|
// which we need to fix, possibly adding a "name:" field to a segment
|
|
@@ -90,12 +93,22 @@ class FullQuery extends pipeline_desc_1.TurtleHeadedPipe {
|
|
|
90
93
|
if (annotation) {
|
|
91
94
|
destQuery.annotation = annotation;
|
|
92
95
|
}
|
|
93
|
-
const pipeOutput =
|
|
96
|
+
const pipeOutput = (0, struct_utils_1.getFinalStruct)(this, structDef, walkPipe);
|
|
94
97
|
pipeFS = new static_space_1.StaticSpace(pipeOutput);
|
|
95
98
|
}
|
|
96
99
|
const appended = this.appendOps(pipeFS, destQuery.pipeline);
|
|
97
100
|
destQuery.pipeline = appended.opList;
|
|
98
|
-
|
|
101
|
+
if (!this.turtleName && this.refinements) {
|
|
102
|
+
const refined = this.refinePipeline(pipeFS, {
|
|
103
|
+
pipeline: destQuery.pipeline,
|
|
104
|
+
}).pipeline;
|
|
105
|
+
destQuery.pipeline = refined;
|
|
106
|
+
const pipeOutput = (0, struct_utils_1.getFinalStruct)(this, structDef, refined);
|
|
107
|
+
return { outputStruct: pipeOutput, query: destQuery };
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
return { outputStruct: appended.structDef, query: destQuery };
|
|
111
|
+
}
|
|
99
112
|
}
|
|
100
113
|
query() {
|
|
101
114
|
return this.queryComp(true).query;
|
|
@@ -181,9 +181,7 @@ class ViewFieldReference extends FieldReference {
|
|
|
181
181
|
super(...arguments);
|
|
182
182
|
this.elementType = 'viewFieldRefernce';
|
|
183
183
|
}
|
|
184
|
-
typecheck(_type) {
|
|
185
|
-
this.log('Cannot use a view field in an expression');
|
|
186
|
-
}
|
|
184
|
+
typecheck(_type) { }
|
|
187
185
|
}
|
|
188
186
|
exports.ViewFieldReference = ViewFieldReference;
|
|
189
187
|
class WildcardFieldReference extends malloy_element_1.MalloyElement {
|
|
@@ -31,6 +31,7 @@ const query_property_interface_1 = require("../types/query-property-interface");
|
|
|
31
31
|
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
|
+
const struct_utils_1 = require("../struct-utils");
|
|
34
35
|
function isTurtle(fd) {
|
|
35
36
|
const ret = fd && typeof fd !== 'string' && fd.type === 'turtle';
|
|
36
37
|
return !!ret;
|
|
@@ -66,16 +67,22 @@ class TurtleDeclRoot extends pipeline_desc_1.TurtleHeadedPipe {
|
|
|
66
67
|
this.log(`Expected '${this.turtleName}' to be a query`);
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
|
-
else if (this.withRefinement) {
|
|
70
|
-
throw this.internalError("Can't refine the head of a view in its definition");
|
|
71
|
-
}
|
|
72
70
|
let pipeOutFS = fs;
|
|
73
71
|
if (modelPipe.pipeline.length > 0) {
|
|
74
|
-
const lastOut =
|
|
72
|
+
const lastOut = (0, struct_utils_1.getFinalStruct)(this, fs.structDef(), modelPipe.pipeline);
|
|
75
73
|
pipeOutFS = new static_space_1.StaticSpace(lastOut);
|
|
76
74
|
}
|
|
77
75
|
const appended = this.appendOps(pipeOutFS, modelPipe.pipeline);
|
|
78
76
|
modelPipe.pipeline = appended.opList;
|
|
77
|
+
if (this.refinements &&
|
|
78
|
+
this.refinements.length > 0 &&
|
|
79
|
+
this.turtleName === undefined) {
|
|
80
|
+
const refined = this.refinePipeline(fs, {
|
|
81
|
+
pipeline: modelPipe.pipeline,
|
|
82
|
+
}).pipeline;
|
|
83
|
+
modelPipe.pipeline = refined;
|
|
84
|
+
return modelPipe;
|
|
85
|
+
}
|
|
79
86
|
return modelPipe;
|
|
80
87
|
}
|
|
81
88
|
getFieldDef(fs, nestParent) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { PipeSegment } from '../../../model/malloy_types';
|
|
2
|
+
import { FieldSpace } from '../types/field-space';
|
|
3
|
+
import { MalloyElement } from '../types/malloy-element';
|
|
4
|
+
import { OpDesc } from '../types/op-desc';
|
|
5
|
+
import { ViewFieldReference } from '../query-items/field-references';
|
|
6
|
+
import { QOPDesc } from './qop-desc';
|
|
7
|
+
export declare abstract class Refinement extends MalloyElement {
|
|
8
|
+
abstract refine(inputFS: FieldSpace, pipeline: PipeSegment[]): PipeSegment[];
|
|
9
|
+
static from(base: QOPDesc | ViewFieldReference): QOPDescRefinement | NamedRefinement;
|
|
10
|
+
}
|
|
11
|
+
export declare class NamedRefinement extends Refinement {
|
|
12
|
+
private readonly name;
|
|
13
|
+
elementType: string;
|
|
14
|
+
constructor(name: ViewFieldReference);
|
|
15
|
+
private getRefinementSegment;
|
|
16
|
+
refine(inputFS: FieldSpace, pipeline: PipeSegment[]): PipeSegment[];
|
|
17
|
+
getOp(inputFS: FieldSpace, _to: PipeSegment): OpDesc;
|
|
18
|
+
}
|
|
19
|
+
export declare class QOPDescRefinement extends Refinement {
|
|
20
|
+
private readonly qOpDesc;
|
|
21
|
+
elementType: string;
|
|
22
|
+
constructor(qOpDesc: QOPDesc);
|
|
23
|
+
private getOp;
|
|
24
|
+
refine(inputFS: FieldSpace, _pipeline: PipeSegment[]): PipeSegment[];
|
|
25
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
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.QOPDescRefinement = exports.NamedRefinement = exports.Refinement = void 0;
|
|
26
|
+
const malloy_element_1 = require("../types/malloy-element");
|
|
27
|
+
const struct_utils_1 = require("../struct-utils");
|
|
28
|
+
const static_space_1 = require("../field-space/static-space");
|
|
29
|
+
const query_property_interface_1 = require("../types/query-property-interface");
|
|
30
|
+
const qop_desc_1 = require("./qop-desc");
|
|
31
|
+
const space_field_1 = require("../types/space-field");
|
|
32
|
+
class Refinement extends malloy_element_1.MalloyElement {
|
|
33
|
+
static from(base) {
|
|
34
|
+
return base instanceof qop_desc_1.QOPDesc
|
|
35
|
+
? new QOPDescRefinement(base)
|
|
36
|
+
: new NamedRefinement(base);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.Refinement = Refinement;
|
|
40
|
+
class NamedRefinement extends Refinement {
|
|
41
|
+
constructor(name) {
|
|
42
|
+
super({ name });
|
|
43
|
+
this.name = name;
|
|
44
|
+
this.elementType = 'namedRefinement';
|
|
45
|
+
}
|
|
46
|
+
getRefinementSegment(fs) {
|
|
47
|
+
const res = this.name.getField(fs);
|
|
48
|
+
if (!res.found) {
|
|
49
|
+
this.name.log(`no such view \`${this.name.refString}\``);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (res.found instanceof space_field_1.SpaceField) {
|
|
53
|
+
const fieldDef = res.found.fieldDef();
|
|
54
|
+
if ((fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.type) === 'turtle') {
|
|
55
|
+
if (fieldDef.pipeline.length > 1 || fieldDef.pipeHead !== undefined) {
|
|
56
|
+
this.name.log(`named refinement \`${this.name.refString}\` must have exactly one stage`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
return fieldDef.pipeline[0];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
this.name.log(`named refinement \`${this.name.refString}\` must be a view, found a ${res.found.describeType().dataType}`);
|
|
63
|
+
}
|
|
64
|
+
refine(inputFS, pipeline) {
|
|
65
|
+
if (pipeline.length === 1) {
|
|
66
|
+
return [this.getOp(inputFS, pipeline[0]).segment];
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.name.log('Named refinements of multi-stage views are not supported');
|
|
70
|
+
// TODO better error pipeline?
|
|
71
|
+
return pipeline;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
getOp(inputFS, _to) {
|
|
75
|
+
var _a, _b;
|
|
76
|
+
const to = { ..._to };
|
|
77
|
+
const from = this.getRefinementSegment(inputFS);
|
|
78
|
+
if (from) {
|
|
79
|
+
if (from.type !== to.type) {
|
|
80
|
+
this.log(`cannot refine ${to.type} view with ${from.type} view`);
|
|
81
|
+
}
|
|
82
|
+
if (from.type !== 'index' && to.type !== 'index') {
|
|
83
|
+
if (from.orderBy !== undefined || from.by !== undefined) {
|
|
84
|
+
if (to.orderBy === undefined && to.by === undefined) {
|
|
85
|
+
if (from.orderBy) {
|
|
86
|
+
to.orderBy = from.orderBy;
|
|
87
|
+
}
|
|
88
|
+
else if (from.by) {
|
|
89
|
+
to.by = from.by;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this.log('refinement cannot override existing ordering');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (from.limit !== undefined) {
|
|
97
|
+
if (to.limit === undefined) {
|
|
98
|
+
to.limit = from.limit;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.log('refinement cannot override existing limit');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
to.filterList =
|
|
106
|
+
to.filterList !== undefined || from.filterList !== undefined
|
|
107
|
+
? [...((_a = to.filterList) !== null && _a !== void 0 ? _a : []), ...((_b = from.filterList) !== null && _b !== void 0 ? _b : [])]
|
|
108
|
+
: undefined;
|
|
109
|
+
const overlappingFields = [];
|
|
110
|
+
const nonOverlappingFields = [];
|
|
111
|
+
const existingNames = new Map(to.fields.map((f) => [
|
|
112
|
+
extractName(f),
|
|
113
|
+
f,
|
|
114
|
+
]));
|
|
115
|
+
for (const field of from.fields) {
|
|
116
|
+
if (existingNames.has(extractName(field))) {
|
|
117
|
+
overlappingFields.push(field);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
nonOverlappingFields.push(field);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
to.fields = [...to.fields, ...nonOverlappingFields];
|
|
124
|
+
if (overlappingFields.length > 0) {
|
|
125
|
+
this.log(`overlapping fields in refinement: ${overlappingFields.map(extractName)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
segment: to,
|
|
130
|
+
outputSpace: () => new static_space_1.StaticSpace((0, struct_utils_1.opOutputStruct)(this.name, inputFS.structDef(), to)),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.NamedRefinement = NamedRefinement;
|
|
135
|
+
function extractName(f1) {
|
|
136
|
+
var _a;
|
|
137
|
+
return typeof f1 === 'string' ? f1 : (_a = f1.as) !== null && _a !== void 0 ? _a : f1.name;
|
|
138
|
+
}
|
|
139
|
+
class QOPDescRefinement extends Refinement {
|
|
140
|
+
constructor(qOpDesc) {
|
|
141
|
+
super({ qOpDesc });
|
|
142
|
+
this.qOpDesc = qOpDesc;
|
|
143
|
+
this.elementType = 'qopdescRefinement';
|
|
144
|
+
}
|
|
145
|
+
getOp(inputFS, headFS, qOpDesc, refineThis) {
|
|
146
|
+
qOpDesc.refineFrom(refineThis);
|
|
147
|
+
return qOpDesc.getOp(inputFS, headFS).segment;
|
|
148
|
+
}
|
|
149
|
+
refine(inputFS, _pipeline) {
|
|
150
|
+
const pipeline = [..._pipeline];
|
|
151
|
+
if (pipeline.length === 1) {
|
|
152
|
+
this.qOpDesc.refineFrom(pipeline[0]);
|
|
153
|
+
return [this.getOp(inputFS, undefined, this.qOpDesc, pipeline[0])];
|
|
154
|
+
}
|
|
155
|
+
const headRefinements = new qop_desc_1.QOPDesc([]);
|
|
156
|
+
const tailRefinements = new qop_desc_1.QOPDesc([]);
|
|
157
|
+
for (const qop of this.qOpDesc.list) {
|
|
158
|
+
switch (qop.queryRefinementStage) {
|
|
159
|
+
case query_property_interface_1.LegalRefinementStage.Head:
|
|
160
|
+
headRefinements.push(qop);
|
|
161
|
+
break;
|
|
162
|
+
case query_property_interface_1.LegalRefinementStage.Single:
|
|
163
|
+
qop.log('Illegal in refinement of a query with more than one stage');
|
|
164
|
+
break;
|
|
165
|
+
case query_property_interface_1.LegalRefinementStage.Tail:
|
|
166
|
+
tailRefinements.push(qop);
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
qop.log('Illegal query refinement');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (headRefinements.notEmpty()) {
|
|
173
|
+
this.has({ headRefinements });
|
|
174
|
+
pipeline[0] = this.getOp(inputFS, undefined, headRefinements, pipeline[0]);
|
|
175
|
+
}
|
|
176
|
+
if (tailRefinements.notEmpty()) {
|
|
177
|
+
const last = pipeline.length - 1;
|
|
178
|
+
this.has({ tailRefinements });
|
|
179
|
+
const finalIn = (0, struct_utils_1.getFinalStruct)(this, inputFS.structDef(), pipeline.slice(-1));
|
|
180
|
+
pipeline[last] = this.getOp(new static_space_1.StaticSpace(finalIn), undefined, tailRefinements, pipeline[last]);
|
|
181
|
+
}
|
|
182
|
+
return pipeline;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.QOPDescRefinement = QOPDescRefinement;
|
|
186
|
+
//# sourceMappingURL=refinements.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FieldDef, PipeSegment, StructDef } from '../../model/malloy_types';
|
|
2
2
|
import { MalloyElement } from './types/malloy-element';
|
|
3
3
|
export declare function opOutputStruct(logTo: MalloyElement, inputStruct: StructDef, opDesc: PipeSegment): StructDef;
|
|
4
|
+
export declare function getFinalStruct(logTo: MalloyElement, walkStruct: StructDef, pipeline: PipeSegment[]): StructDef;
|
|
4
5
|
export declare function getStructFieldDef(s: StructDef, fn: string): FieldDef | undefined;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.getStructFieldDef = exports.opOutputStruct = void 0;
|
|
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
28
|
const error_factory_1 = require("./error-factory");
|
|
@@ -41,6 +41,13 @@ function opOutputStruct(logTo, inputStruct, opDesc) {
|
|
|
41
41
|
return { ...error_factory_1.ErrorFactory.structDef, dialect: inputStruct.dialect };
|
|
42
42
|
}
|
|
43
43
|
exports.opOutputStruct = opOutputStruct;
|
|
44
|
+
function getFinalStruct(logTo, walkStruct, pipeline) {
|
|
45
|
+
for (const modelQop of pipeline) {
|
|
46
|
+
walkStruct = opOutputStruct(logTo, walkStruct, modelQop);
|
|
47
|
+
}
|
|
48
|
+
return walkStruct;
|
|
49
|
+
}
|
|
50
|
+
exports.getFinalStruct = getFinalStruct;
|
|
44
51
|
function getStructFieldDef(s, fn) {
|
|
45
52
|
return s.fields.find(fld => (fld.as || fld.name) === fn);
|
|
46
53
|
}
|
|
@@ -53,7 +53,7 @@ export declare abstract class MalloyElement {
|
|
|
53
53
|
private varInfo;
|
|
54
54
|
protected internalError(msg: string): Error;
|
|
55
55
|
needs(doc: Document): ModelDataRequest | undefined;
|
|
56
|
-
inExperiment(experimentID: string): boolean;
|
|
56
|
+
inExperiment(experimentID: string, silent?: boolean): boolean;
|
|
57
57
|
}
|
|
58
58
|
export declare class Unimplemented extends MalloyElement {
|
|
59
59
|
elementType: string;
|
|
@@ -262,14 +262,16 @@ class MalloyElement {
|
|
|
262
262
|
return childNeeds;
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
|
-
inExperiment(experimentID) {
|
|
265
|
+
inExperiment(experimentID, silent = false) {
|
|
266
266
|
var _a;
|
|
267
267
|
const experimental = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.compilerFlags.tag('experimental');
|
|
268
268
|
const enabled = experimental && (experimental.bare() || experimental.has(experimentID));
|
|
269
269
|
if (enabled) {
|
|
270
270
|
return true;
|
|
271
271
|
}
|
|
272
|
-
|
|
272
|
+
if (!silent) {
|
|
273
|
+
this.log(`Experimental flag '${experimentID}' is not set, feature not available`);
|
|
274
|
+
}
|
|
273
275
|
return false;
|
|
274
276
|
}
|
|
275
277
|
}
|