@malloydata/malloy 0.0.275 → 0.0.276
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/field-space/query-spaces.js +1 -1
- package/dist/lang/ast/source-elements/composite-source.js +117 -38
- package/dist/lang/parse-log.d.ts +1 -0
- package/dist/model/composite_source_utils.d.ts +5 -0
- package/dist/model/composite_source_utils.js +43 -15
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -171,7 +171,7 @@ class QueryOperationSpace extends refined_space_1.RefinedSpace {
|
|
|
171
171
|
var _a;
|
|
172
172
|
const reference = joinPath.map(n => new field_space_1.FieldName(n));
|
|
173
173
|
this.astEl.has({ reference });
|
|
174
|
-
const lookup = this.exprSpace.lookup(reference);
|
|
174
|
+
const lookup = this.exprSpace.lookup(reference, 'private');
|
|
175
175
|
// Should always be found...
|
|
176
176
|
if (lookup.found && lookup.found instanceof struct_space_field_base_1.StructSpaceFieldBase) {
|
|
177
177
|
return (0, composite_source_utils_1.joinedFieldUsage)(joinPath.slice(0, -1), (_a = lookup.found.fieldDef().onFieldUsage) !== null && _a !== void 0 ? _a : (0, composite_source_utils_1.emptyFieldUsage)());
|
|
@@ -23,29 +23,68 @@ class CompositeSource extends source_1.Source {
|
|
|
23
23
|
return this.withParameters(parameterSpace, []);
|
|
24
24
|
}
|
|
25
25
|
withParameters(parameterSpace, pList) {
|
|
26
|
-
const sourceDefs = this.sources.map(source =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
const sourceDefs = this.sources.map(source => {
|
|
27
|
+
return {
|
|
28
|
+
sourceDef: source.withParameters(parameterSpace, pList),
|
|
29
|
+
logTo: source,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
return composeSources(sourceDefs, this);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.CompositeSource = CompositeSource;
|
|
36
|
+
function composeSources(sources, compositeCodeSource) {
|
|
37
|
+
const connection = sources[0].sourceDef.connection;
|
|
38
|
+
const dialect = sources[0].sourceDef.dialect;
|
|
39
|
+
const name = 'composite_source';
|
|
40
|
+
const fieldsByName = new Map();
|
|
41
|
+
const joinsToCompose = new Map();
|
|
42
|
+
sources.forEach(source => {
|
|
43
|
+
var _a;
|
|
44
|
+
const sourceDef = source.sourceDef;
|
|
45
|
+
// Check that connections all match; don't bother checking dialect, since it will
|
|
46
|
+
// match if the connection matches.
|
|
47
|
+
if (sourceDef.connection !== connection) {
|
|
48
|
+
source.logTo.logError('composite-source-connection-mismatch', `All sources in a composite source must share the same connection; connection \`${sourceDef.connection}\` differs from previous connection \`${connection}\``);
|
|
49
|
+
}
|
|
50
|
+
for (const field of sourceDef.fields) {
|
|
51
|
+
const fieldName = (_a = field.as) !== null && _a !== void 0 ? _a : field.name;
|
|
52
|
+
if (field.accessModifier === 'private') {
|
|
53
|
+
continue;
|
|
39
54
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
/**
|
|
56
|
+
* compose(flights -> {nest: by_carrier})
|
|
57
|
+
*
|
|
58
|
+
*
|
|
59
|
+
*
|
|
60
|
+
*
|
|
61
|
+
*/
|
|
62
|
+
// does not handle compose(flights -> {nest: by_carrier})
|
|
63
|
+
if ((0, malloy_types_1.isJoined)(field) && (0, malloy_types_1.isSourceDef)(field)) {
|
|
64
|
+
const existingJoins = joinsToCompose.get(fieldName);
|
|
65
|
+
if (existingJoins) {
|
|
66
|
+
existingJoins.sources.push(field);
|
|
67
|
+
if (field.join !== existingJoins.join) {
|
|
68
|
+
source.logTo.logError('composite-source-connection-mismatch', `Composited joins must have the same join type; \`${field.join}\` differs from previous type \`${existingJoins.join}\``);
|
|
69
|
+
}
|
|
70
|
+
if (field.matrixOperation !== existingJoins.matrixOperation) {
|
|
71
|
+
source.logTo.logError('composite-source-connection-mismatch', `Composited joins must have the same matrix operation; \`${field.matrixOperation}\` differs from previous operation \`${existingJoins.matrixOperation}\``);
|
|
72
|
+
}
|
|
73
|
+
if (field.accessModifier === 'internal') {
|
|
74
|
+
existingJoins.accessModifier = 'internal';
|
|
75
|
+
}
|
|
44
76
|
}
|
|
45
|
-
|
|
46
|
-
|
|
77
|
+
else {
|
|
78
|
+
joinsToCompose.set(fieldName, {
|
|
79
|
+
sources: [field],
|
|
80
|
+
join: field.join,
|
|
81
|
+
matrixOperation: field.matrixOperation,
|
|
82
|
+
accessModifier: field.accessModifier,
|
|
83
|
+
});
|
|
47
84
|
}
|
|
48
|
-
|
|
85
|
+
// TODO ensure that there isn't also a normal field with this name...
|
|
86
|
+
}
|
|
87
|
+
else if ((0, malloy_types_1.isAtomic)(field)) {
|
|
49
88
|
const existing = fieldsByName.get(fieldName);
|
|
50
89
|
if (existing === undefined) {
|
|
51
90
|
const compositeField = {
|
|
@@ -53,32 +92,72 @@ class CompositeSource extends source_1.Source {
|
|
|
53
92
|
name: fieldName,
|
|
54
93
|
as: undefined,
|
|
55
94
|
e: { node: 'compositeField' },
|
|
56
|
-
fieldUsage: [
|
|
57
|
-
|
|
58
|
-
|
|
95
|
+
fieldUsage: [
|
|
96
|
+
{ path: [fieldName], at: compositeCodeSource.codeLocation },
|
|
97
|
+
],
|
|
98
|
+
code: compositeCodeSource.code,
|
|
99
|
+
location: compositeCodeSource.codeLocation,
|
|
59
100
|
// A composite field's grouping may differ from slice to slice
|
|
60
101
|
requiresGroupBy: undefined,
|
|
61
102
|
};
|
|
62
103
|
fieldsByName.set(fieldName, compositeField);
|
|
63
|
-
fields.push(compositeField);
|
|
64
104
|
}
|
|
65
|
-
else
|
|
66
|
-
|
|
105
|
+
else {
|
|
106
|
+
if (field.accessModifier === 'internal') {
|
|
107
|
+
existing.accessModifier = 'internal';
|
|
108
|
+
}
|
|
109
|
+
if (!(malloy_types_1.TD.eq(field, existing) ||
|
|
110
|
+
// Handle the case where both fields don't have a raw type...
|
|
111
|
+
// TODO ask MToy about this
|
|
112
|
+
(field.type === 'sql native' &&
|
|
113
|
+
existing.type === 'sql native' &&
|
|
114
|
+
field.rawType === existing.rawType))) {
|
|
115
|
+
source.logTo.logError('composite-field-type-mismatch', `field \`${field.name}\` must have the same type in all composite inputs: ${prettyType(field)} does not match ${prettyType(existing)}`);
|
|
116
|
+
}
|
|
67
117
|
}
|
|
68
118
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// TODO
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
119
|
+
else {
|
|
120
|
+
source.logTo.logWarning('composite-source-atomic-fields-only', `Only atomic fields are supported in composite sources; field \`${field.name}\` is not atomic and will be ignored`);
|
|
121
|
+
}
|
|
122
|
+
// TODO actually typecheck the existing field against the new field...
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
for (const [joinName, sourcesInJoin] of joinsToCompose.entries()) {
|
|
126
|
+
if (fieldsByName.has(joinName)) {
|
|
127
|
+
compositeCodeSource.logError('composite-field-type-mismatch', `field \`${joinName}\` must be a join in all sources or none`);
|
|
128
|
+
}
|
|
129
|
+
const composedSource = composeSources(sourcesInJoin.sources.map(s => ({
|
|
130
|
+
sourceDef: s,
|
|
131
|
+
logTo: compositeCodeSource,
|
|
132
|
+
})), compositeCodeSource);
|
|
133
|
+
const compositeJoin = {
|
|
134
|
+
...composedSource,
|
|
135
|
+
// We don't need to store the sources, since this is just a placeholder for typechecking
|
|
136
|
+
// the composite resolver will use the joins from the original input sources regardless.
|
|
137
|
+
sources: [],
|
|
138
|
+
join: sourcesInJoin.join,
|
|
139
|
+
name: joinName,
|
|
140
|
+
matrixOperation: sourcesInJoin.matrixOperation,
|
|
141
|
+
onExpression: undefined,
|
|
142
|
+
onCompositeFieldUsage: [],
|
|
143
|
+
accessModifier: sourcesInJoin.accessModifier,
|
|
80
144
|
};
|
|
145
|
+
fieldsByName.set(joinName, compositeJoin);
|
|
81
146
|
}
|
|
147
|
+
return {
|
|
148
|
+
type: 'composite',
|
|
149
|
+
// TODO Use sourceRefs rather than sourceDefs when possible to avoid potential
|
|
150
|
+
// explosion of source defs...
|
|
151
|
+
sources: sources.map(s => s.sourceDef),
|
|
152
|
+
connection,
|
|
153
|
+
fields: [...fieldsByName.values()],
|
|
154
|
+
dialect,
|
|
155
|
+
name,
|
|
156
|
+
// TODO actually compose the parameters?
|
|
157
|
+
parameters: sources[0].sourceDef.parameters,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function prettyType(a) {
|
|
161
|
+
return `\`${a.type}\``;
|
|
82
162
|
}
|
|
83
|
-
exports.CompositeSource = CompositeSource;
|
|
84
163
|
//# sourceMappingURL=composite-source.js.map
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -157,6 +157,7 @@ type MessageParameterTypes = {
|
|
|
157
157
|
'field-list-edit-not-found': string;
|
|
158
158
|
'unexpected-element-type': string;
|
|
159
159
|
'field-not-found': string;
|
|
160
|
+
'composite-field-type-mismatch': string;
|
|
160
161
|
'invalid-composite-source-input': string;
|
|
161
162
|
'invalid-composite-field-usage': {
|
|
162
163
|
newUsage: FieldUsage[];
|
|
@@ -100,9 +100,21 @@ sources) {
|
|
|
100
100
|
filterList: [...((_b = source.filterList) !== null && _b !== void 0 ? _b : []), ...((_c = base.filterList) !== null && _c !== void 0 ? _c : [])],
|
|
101
101
|
};
|
|
102
102
|
const joinError = processJoins(path, base, rootFields, nests, expandedCategorized);
|
|
103
|
-
// Fourth point where we abort: if a join failed
|
|
104
|
-
if (joinError.
|
|
105
|
-
|
|
103
|
+
// Fourth point where we abort: if a join failed
|
|
104
|
+
if (joinError.errors.length > 0) {
|
|
105
|
+
for (const error of joinError.errors) {
|
|
106
|
+
if (error.error.code !== 'no_suitable_composite_source_input') {
|
|
107
|
+
return { error: error.error };
|
|
108
|
+
}
|
|
109
|
+
fail({
|
|
110
|
+
type: 'join-failed',
|
|
111
|
+
failures: error.error.data.failures,
|
|
112
|
+
path: error.error.data.path,
|
|
113
|
+
firstUsage: error.firstUsage,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
abort();
|
|
117
|
+
continue overSources;
|
|
106
118
|
}
|
|
107
119
|
joinsProcessed = true;
|
|
108
120
|
if (nests !== undefined) {
|
|
@@ -146,8 +158,8 @@ sources) {
|
|
|
146
158
|
};
|
|
147
159
|
}
|
|
148
160
|
const joinResult = processJoins(path, base, rootFields, nests, categorizeFieldUsage(expanded.result));
|
|
149
|
-
if (joinResult.
|
|
150
|
-
return { error: joinResult.error };
|
|
161
|
+
if (joinResult.errors.length > 0) {
|
|
162
|
+
return { error: joinResult.errors[0].error };
|
|
151
163
|
}
|
|
152
164
|
anyComposites || (anyComposites = joinResult.anyComposites);
|
|
153
165
|
}
|
|
@@ -290,6 +302,7 @@ function processJoins(path, base, rootFields, nests, categorizedFieldUsage) {
|
|
|
290
302
|
var _a, _b;
|
|
291
303
|
let anyComposites = false;
|
|
292
304
|
const fieldsByName = {};
|
|
305
|
+
const errors = [];
|
|
293
306
|
for (const field of base.fields) {
|
|
294
307
|
fieldsByName[(_a = field.as) !== null && _a !== void 0 ? _a : field.name] = field;
|
|
295
308
|
}
|
|
@@ -297,20 +310,24 @@ function processJoins(path, base, rootFields, nests, categorizedFieldUsage) {
|
|
|
297
310
|
const newPath = [...path, joinName];
|
|
298
311
|
const join = fieldsByName[joinName];
|
|
299
312
|
if (join === undefined) {
|
|
300
|
-
|
|
313
|
+
errors.push({
|
|
301
314
|
error: {
|
|
302
315
|
code: 'composite_source_not_defined',
|
|
303
316
|
data: { path: newPath },
|
|
304
317
|
},
|
|
305
|
-
|
|
318
|
+
firstUsage: joinedUsage[0],
|
|
319
|
+
});
|
|
320
|
+
continue;
|
|
306
321
|
}
|
|
307
322
|
if (!(0, malloy_types_1.isJoined)(join)) {
|
|
308
|
-
|
|
323
|
+
errors.push({
|
|
309
324
|
error: {
|
|
310
325
|
code: 'composite_source_not_a_join',
|
|
311
326
|
data: { path: newPath },
|
|
312
327
|
},
|
|
313
|
-
|
|
328
|
+
firstUsage: joinedUsage[0],
|
|
329
|
+
});
|
|
330
|
+
continue;
|
|
314
331
|
}
|
|
315
332
|
else if (!(0, malloy_types_1.isSourceDef)(join)) {
|
|
316
333
|
// Non-source join, like an array, skip it (no need to resolve)
|
|
@@ -318,19 +335,25 @@ function processJoins(path, base, rootFields, nests, categorizedFieldUsage) {
|
|
|
318
335
|
}
|
|
319
336
|
const resolved = _resolveCompositeSources(newPath, join, genRootFields(rootFields, path, base.fields), nests, joinedUsage);
|
|
320
337
|
if ('error' in resolved) {
|
|
321
|
-
|
|
338
|
+
errors.push({
|
|
339
|
+
error: resolved.error,
|
|
340
|
+
firstUsage: joinedUsage[0],
|
|
341
|
+
});
|
|
342
|
+
continue;
|
|
322
343
|
}
|
|
323
344
|
if (!resolved.anyComposites) {
|
|
324
345
|
continue;
|
|
325
346
|
}
|
|
326
347
|
anyComposites = true;
|
|
327
348
|
if (!(0, malloy_types_1.isJoinable)(resolved.success)) {
|
|
328
|
-
|
|
349
|
+
errors.push({
|
|
329
350
|
error: {
|
|
330
351
|
code: 'composite_source_is_not_joinable',
|
|
331
352
|
data: { path: newPath },
|
|
332
353
|
},
|
|
333
|
-
|
|
354
|
+
firstUsage: joinedUsage[0],
|
|
355
|
+
});
|
|
356
|
+
continue;
|
|
334
357
|
}
|
|
335
358
|
fieldsByName[joinName] = {
|
|
336
359
|
...resolved.success,
|
|
@@ -340,7 +363,7 @@ function processJoins(path, base, rootFields, nests, categorizedFieldUsage) {
|
|
|
340
363
|
};
|
|
341
364
|
base.fields = Object.values(fieldsByName);
|
|
342
365
|
}
|
|
343
|
-
return { anyComposites };
|
|
366
|
+
return { anyComposites, errors };
|
|
344
367
|
}
|
|
345
368
|
function resolveCompositeSources(source, segment, fieldUsage) {
|
|
346
369
|
var _a;
|
|
@@ -449,7 +472,8 @@ function fieldUsageJoinPaths(fieldUsage) {
|
|
|
449
472
|
exports.fieldUsageJoinPaths = fieldUsageJoinPaths;
|
|
450
473
|
function isCompositeField(fieldDef) {
|
|
451
474
|
var _a;
|
|
452
|
-
return 'e' in fieldDef && ((_a = fieldDef.e) === null || _a === void 0 ? void 0 : _a.node) === 'compositeField'
|
|
475
|
+
return (('e' in fieldDef && ((_a = fieldDef.e) === null || _a === void 0 ? void 0 : _a.node) === 'compositeField') ||
|
|
476
|
+
((0, malloy_types_1.isJoined)(fieldDef) && fieldDef.type === 'composite'));
|
|
453
477
|
}
|
|
454
478
|
function getNonCompositeFields(source) {
|
|
455
479
|
return source.fields.filter(f => !isCompositeField(f));
|
|
@@ -773,10 +797,14 @@ function logCompositeError(error, logTo) {
|
|
|
773
797
|
const fieldRef = `\`${issue.field.path.join('.')}\``;
|
|
774
798
|
logTo.logError('could-not-resolve-composite-source', `Could not resolve composite source: missing field ${fieldRef} in ${source}${requiredFields}`, { at: issue.field.at });
|
|
775
799
|
}
|
|
776
|
-
else {
|
|
800
|
+
else if (issue.type === 'missing-required-group-by') {
|
|
777
801
|
const fieldRef = `\`${issue.requiredGroupBy.path.join('.')}\``;
|
|
778
802
|
logTo.logError('could-not-resolve-composite-source', `Could not resolve composite source: missing group by ${fieldRef} as required in ${source}${requiredFields}`, { at: issue.requiredGroupBy.at });
|
|
779
803
|
}
|
|
804
|
+
else {
|
|
805
|
+
const joinRef = `\`${issue.path.join('.')}\``;
|
|
806
|
+
logTo.logError('could-not-resolve-composite-source', `Could not resolve composite source: join ${joinRef} could not be resolved in ${source}${requiredFields}`, { at: issue.firstUsage.at });
|
|
807
|
+
}
|
|
780
808
|
}
|
|
781
809
|
}
|
|
782
810
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MALLOY_VERSION = "0.0.
|
|
1
|
+
export declare const MALLOY_VERSION = "0.0.276";
|
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MALLOY_VERSION = void 0;
|
|
4
4
|
// generated with 'generate-version-file' script; do not edit manually
|
|
5
|
-
exports.MALLOY_VERSION = '0.0.
|
|
5
|
+
exports.MALLOY_VERSION = '0.0.276';
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/malloy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.276",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@malloydata/malloy-filter": "0.0.
|
|
45
|
-
"@malloydata/malloy-interfaces": "0.0.
|
|
46
|
-
"@malloydata/malloy-tag": "0.0.
|
|
44
|
+
"@malloydata/malloy-filter": "0.0.276",
|
|
45
|
+
"@malloydata/malloy-interfaces": "0.0.276",
|
|
46
|
+
"@malloydata/malloy-tag": "0.0.276",
|
|
47
47
|
"antlr4ts": "^0.5.0-alpha.4",
|
|
48
48
|
"assert": "^2.0.0",
|
|
49
49
|
"jaro-winkler": "^0.2.8",
|