@malloydata/malloy 0.0.23 → 0.0.24-dev230215185525
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/expressions/expr-id-reference.js +2 -4
- package/dist/lang/ast/expressions/expr-logical-op.d.ts +1 -0
- package/dist/lang/ast/expressions/expr-not.js +6 -9
- package/dist/lang/ast/types/expression-def.js +48 -0
- package/dist/lang/ast/types/space-field.js +5 -0
- package/dist/lang/ast/types/type-desc.d.ts +1 -0
- package/dist/lang/test/field-symbols.spec.js +11 -0
- package/dist/lang/test/parse.spec.js +32 -0
- package/dist/lang/test/test-translator.js +3 -1
- package/dist/malloy.d.ts +2 -1
- package/dist/malloy.js +3 -0
- package/dist/model/malloy_query.js +7 -0
- package/dist/model/malloy_types.d.ts +7 -2
- package/dist/model/malloy_types.js +9 -1
- package/package.json +1 -1
|
@@ -41,10 +41,8 @@ class ExprIdReference extends expression_def_1.ExpressionDef {
|
|
|
41
41
|
const def = this.fieldReference.getField(fs);
|
|
42
42
|
if (def.found) {
|
|
43
43
|
const value = [{ "type": def.found.refType, "path": this.refString }];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
value
|
|
47
|
-
};
|
|
44
|
+
const td = def.found.typeDesc();
|
|
45
|
+
return { ...td, value };
|
|
48
46
|
}
|
|
49
47
|
this.log(def.error);
|
|
50
48
|
return (0, ast_utils_1.errorFor)(def.error);
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.ExprNot = void 0;
|
|
26
|
-
const ast_utils_1 = require("../ast-utils");
|
|
27
26
|
const fragtype_utils_1 = require("../fragtype-utils");
|
|
28
27
|
const unary_1 = require("./unary");
|
|
29
28
|
const utils_1 = require("./utils");
|
|
@@ -35,14 +34,12 @@ class ExprNot extends unary_1.Unary {
|
|
|
35
34
|
}
|
|
36
35
|
getExpression(fs) {
|
|
37
36
|
const notThis = this.expr.getExpression(fs);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
return (0, ast_utils_1.errorFor)("not requires boolean");
|
|
37
|
+
const doNot = this.typeCheck(this.expr, notThis);
|
|
38
|
+
return {
|
|
39
|
+
...notThis,
|
|
40
|
+
"dataType": "boolean",
|
|
41
|
+
"value": doNot ? (0, utils_1.nullsafeNot)(notThis.value) : ["false"]
|
|
42
|
+
};
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
exports.ExprNot = ExprNot;
|
|
@@ -197,6 +197,18 @@ function nullCompare(left, op, right) {
|
|
|
197
197
|
function equality(fs, left, op, right) {
|
|
198
198
|
const lhs = left.getExpression(fs);
|
|
199
199
|
const rhs = right.getExpression(fs);
|
|
200
|
+
// Unsupported types can be compare with null
|
|
201
|
+
const checkUnsupport = lhs.dataType === "unsupported" || rhs.dataType === "unsupported";
|
|
202
|
+
if (checkUnsupport) {
|
|
203
|
+
const oneNull = lhs.dataType === "null" || rhs.dataType === "null";
|
|
204
|
+
const rawMatch = lhs.rawType && lhs.rawType === rhs.rawType;
|
|
205
|
+
if (!(oneNull || rawMatch)) {
|
|
206
|
+
const noGo = unsupportError(left, lhs, right, rhs);
|
|
207
|
+
if (noGo) {
|
|
208
|
+
return { ...noGo, "dataType": "boolean" };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
200
212
|
let value = timeCompare(lhs, op, rhs) || (0, utils_1.compose)(lhs.value, op, rhs.value);
|
|
201
213
|
if (lhs.dataType != "unknown" && rhs.dataType != "unknown") {
|
|
202
214
|
switch (op) {
|
|
@@ -238,6 +250,10 @@ function compare(fs, left, op, right) {
|
|
|
238
250
|
const lhs = left.getExpression(fs);
|
|
239
251
|
const rhs = right.getExpression(fs);
|
|
240
252
|
const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
|
|
253
|
+
const noCompare = unsupportError(left, lhs, right, rhs);
|
|
254
|
+
if (noCompare) {
|
|
255
|
+
return { ...noCompare, "dataType": "boolean" };
|
|
256
|
+
}
|
|
241
257
|
const value = timeCompare(lhs, op, rhs) || (0, utils_1.compose)(lhs.value, op, rhs.value);
|
|
242
258
|
return {
|
|
243
259
|
"dataType": "boolean",
|
|
@@ -259,6 +275,10 @@ function allAre(oneType, ...values) {
|
|
|
259
275
|
function numeric(fs, left, op, right) {
|
|
260
276
|
const lhs = left.getExpression(fs);
|
|
261
277
|
const rhs = right.getExpression(fs);
|
|
278
|
+
const noGo = unsupportError(left, lhs, right, rhs);
|
|
279
|
+
if (noGo) {
|
|
280
|
+
return noGo;
|
|
281
|
+
}
|
|
262
282
|
const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
|
|
263
283
|
if (allAre("number", lhs, rhs)) {
|
|
264
284
|
return {
|
|
@@ -273,6 +293,10 @@ function numeric(fs, left, op, right) {
|
|
|
273
293
|
function delta(fs, left, op, right) {
|
|
274
294
|
const lhs = left.getExpression(fs);
|
|
275
295
|
const rhs = right.getExpression(fs);
|
|
296
|
+
const noGo = unsupportError(left, lhs, right, rhs);
|
|
297
|
+
if (noGo) {
|
|
298
|
+
return noGo;
|
|
299
|
+
}
|
|
276
300
|
if ((0, malloy_types_1.isTimeFieldType)(lhs.dataType)) {
|
|
277
301
|
let duration = right;
|
|
278
302
|
if (rhs.dataType !== "duration") {
|
|
@@ -318,6 +342,11 @@ function applyBinary(fs, left, op, right) {
|
|
|
318
342
|
if (oneOf(op, "/")) {
|
|
319
343
|
const num = left.getExpression(fs);
|
|
320
344
|
const denom = right.getExpression(fs);
|
|
345
|
+
const noGo = unsupportError(left, num, right, denom);
|
|
346
|
+
if (noGo) {
|
|
347
|
+
left.log("Cannot operate with unsupported type");
|
|
348
|
+
return noGo;
|
|
349
|
+
}
|
|
321
350
|
if (num.dataType != "number") {
|
|
322
351
|
left.log("Numerator for division must be a number");
|
|
323
352
|
}
|
|
@@ -343,4 +372,23 @@ function applyBinary(fs, left, op, right) {
|
|
|
343
372
|
return (0, ast_utils_1.errorFor)("applybinary bad operator");
|
|
344
373
|
}
|
|
345
374
|
exports.applyBinary = applyBinary;
|
|
375
|
+
/**
|
|
376
|
+
* Return an error if an binary operation includes unsupported types.
|
|
377
|
+
*/
|
|
378
|
+
function unsupportError(l, lhs, r, rhs) {
|
|
379
|
+
const ret = {
|
|
380
|
+
"dataType": lhs.dataType,
|
|
381
|
+
"expressionType": (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType),
|
|
382
|
+
"value": ["'unsupported operation'"]
|
|
383
|
+
};
|
|
384
|
+
if (lhs.dataType === "unsupported") {
|
|
385
|
+
l.log("Unsupported type not allowed in expression");
|
|
386
|
+
return { ...ret, "dataType": rhs.dataType };
|
|
387
|
+
}
|
|
388
|
+
if (rhs.dataType === "unsupported") {
|
|
389
|
+
r.log("Unsupported type not allowed in expression");
|
|
390
|
+
return ret;
|
|
391
|
+
}
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
346
394
|
//# sourceMappingURL=expression-def.js.map
|
|
@@ -35,6 +35,11 @@ class SpaceField extends space_entry_1.SpaceEntry {
|
|
|
35
35
|
if ((0, malloy_types_1.isFieldTypeDef)(def) && def.expressionType) {
|
|
36
36
|
ref.expressionType = def.expressionType;
|
|
37
37
|
}
|
|
38
|
+
if (ref.dataType === "unsupported" &&
|
|
39
|
+
def.type === "unsupported" &&
|
|
40
|
+
def.rawType) {
|
|
41
|
+
ref.rawType = def.rawType;
|
|
42
|
+
}
|
|
38
43
|
return ref;
|
|
39
44
|
}
|
|
40
45
|
getQueryFieldDef(_fs) {
|
|
@@ -91,6 +91,17 @@ describe("structdef comprehension", () => {
|
|
|
91
91
|
const oField = space.structDef().fields[0];
|
|
92
92
|
expect(oField).toEqual(field);
|
|
93
93
|
});
|
|
94
|
+
test(`import unsupported field`, () => {
|
|
95
|
+
const field = {
|
|
96
|
+
"name": "t",
|
|
97
|
+
"type": "unsupported"
|
|
98
|
+
};
|
|
99
|
+
const struct = mkStructDef(field);
|
|
100
|
+
const space = new static_space_1.StaticSpace(struct);
|
|
101
|
+
expect(space.lookup(fieldRef("t")).found).toBeInstanceOf(column_space_field_1.ColumnSpaceField);
|
|
102
|
+
const oField = space.structDef().fields[0];
|
|
103
|
+
expect(oField).toEqual(field);
|
|
104
|
+
});
|
|
94
105
|
test(`import nested field`, () => {
|
|
95
106
|
const field = {
|
|
96
107
|
"name": "t",
|
|
@@ -1088,6 +1088,38 @@ describe("expressions", () => {
|
|
|
1088
1088
|
}
|
|
1089
1089
|
});
|
|
1090
1090
|
});
|
|
1091
|
+
describe("unspported fields in schema", () => {
|
|
1092
|
+
test("unsupported reference in result allowed", () => {
|
|
1093
|
+
const uModel = new BetaModel(`query: a->{ group_by: aun }`);
|
|
1094
|
+
expect(uModel).modelCompiled();
|
|
1095
|
+
});
|
|
1096
|
+
test("unsupported reference can be compared to NULL", () => {
|
|
1097
|
+
const uModel = new BetaModel(`query: a->{ where: aun != NULL; project: * }`);
|
|
1098
|
+
expect(uModel).modelCompiled();
|
|
1099
|
+
});
|
|
1100
|
+
test("flag unsupported equality", () => {
|
|
1101
|
+
// because we don't know if the two unsupported types are comparable
|
|
1102
|
+
const uModel = new BetaModel(`query: ab->{ where: aun = b.aun project: * }`);
|
|
1103
|
+
expect(uModel).compileToFailWith("Unsupported type not allowed in expression");
|
|
1104
|
+
});
|
|
1105
|
+
test("flag unsupported compare", () => {
|
|
1106
|
+
// because we don't know if the two unsupported types are comparable
|
|
1107
|
+
const uModel = new BetaModel(`query: ab->{ where: aun > b.aun project: * }`);
|
|
1108
|
+
expect(uModel).compileToFailWith("Unsupported type not allowed in expression");
|
|
1109
|
+
});
|
|
1110
|
+
test("allow unsupported equality when raw types match", () => {
|
|
1111
|
+
const uModel = new BetaModel(`query: ab->{ where: aweird = b.aweird project: * }`);
|
|
1112
|
+
expect(uModel).modelCompiled();
|
|
1113
|
+
});
|
|
1114
|
+
test("flag not applied to unsupported", () => {
|
|
1115
|
+
const uModel = new BetaModel(`source: x is a { dimension: notUn is not aun }`);
|
|
1116
|
+
expect(uModel).compileToFailWith("'not' Can't use type unsupported");
|
|
1117
|
+
});
|
|
1118
|
+
test("allow unsupported to be cast", () => {
|
|
1119
|
+
const uModel = new BetaModel(`source: x is a { dimension: notUn is aun::string }`);
|
|
1120
|
+
expect(uModel).modelCompiled();
|
|
1121
|
+
});
|
|
1122
|
+
});
|
|
1091
1123
|
describe("sql:", () => {
|
|
1092
1124
|
function makeSchemaResponse(sql) {
|
|
1093
1125
|
const cname = sql.connection || "bigquery";
|
|
@@ -45,7 +45,9 @@ const mockSchema = {
|
|
|
45
45
|
{ "type": "number", "name": "ai", "numberType": "integer" },
|
|
46
46
|
{ "type": "date", "name": "ad" },
|
|
47
47
|
{ "type": "boolean", "name": "abool" },
|
|
48
|
-
{ "type": "timestamp", "name": "ats" }
|
|
48
|
+
{ "type": "timestamp", "name": "ats" },
|
|
49
|
+
{ "type": "unsupported", "name": "aun" },
|
|
50
|
+
{ "type": "unsupported", "name": "aweird", "rawType": "weird" }
|
|
49
51
|
]
|
|
50
52
|
},
|
|
51
53
|
"malloytest.carriers": {
|
package/dist/malloy.d.ts
CHANGED
|
@@ -494,7 +494,8 @@ export declare enum AtomicFieldType {
|
|
|
494
494
|
Boolean = "boolean",
|
|
495
495
|
Date = "date",
|
|
496
496
|
Timestamp = "timestamp",
|
|
497
|
-
Json = "json"
|
|
497
|
+
Json = "json",
|
|
498
|
+
Unsupported = "unsupported"
|
|
498
499
|
}
|
|
499
500
|
export declare class AtomicField extends Entity {
|
|
500
501
|
protected fieldTypeDef: FieldTypeDef;
|
package/dist/malloy.js
CHANGED
|
@@ -986,6 +986,7 @@ var AtomicFieldType;
|
|
|
986
986
|
AtomicFieldType["Date"] = "date";
|
|
987
987
|
AtomicFieldType["Timestamp"] = "timestamp";
|
|
988
988
|
AtomicFieldType["Json"] = "json";
|
|
989
|
+
AtomicFieldType["Unsupported"] = "unsupported";
|
|
989
990
|
})(AtomicFieldType = exports.AtomicFieldType || (exports.AtomicFieldType = {}));
|
|
990
991
|
class AtomicField extends Entity {
|
|
991
992
|
constructor(fieldTypeDef, parent, source) {
|
|
@@ -1007,6 +1008,8 @@ class AtomicField extends Entity {
|
|
|
1007
1008
|
return AtomicFieldType.Number;
|
|
1008
1009
|
case "json":
|
|
1009
1010
|
return AtomicFieldType.Json;
|
|
1011
|
+
case "unsupported":
|
|
1012
|
+
return AtomicFieldType.Unsupported;
|
|
1010
1013
|
}
|
|
1011
1014
|
}
|
|
1012
1015
|
isIntrinsic() {
|
|
@@ -519,6 +519,8 @@ class QueryFieldBoolean extends QueryAtomicField {
|
|
|
519
519
|
}
|
|
520
520
|
class QueryFieldJSON extends QueryAtomicField {
|
|
521
521
|
}
|
|
522
|
+
class QueryFieldUnsupported extends QueryAtomicField {
|
|
523
|
+
}
|
|
522
524
|
// in a query a struct can be referenced. The struct will
|
|
523
525
|
// emit the primary key field in the actual result set and
|
|
524
526
|
// will include the StructDef as a foreign key join in the output
|
|
@@ -1638,6 +1640,9 @@ class QueryQuery extends QueryField {
|
|
|
1638
1640
|
location
|
|
1639
1641
|
});
|
|
1640
1642
|
break;
|
|
1643
|
+
case "unsupported":
|
|
1644
|
+
fields.push({ ...fi.f.fieldDef, resultMetadata, location });
|
|
1645
|
+
break;
|
|
1641
1646
|
default:
|
|
1642
1647
|
throw new Error(`unknown Field Type in query ${JSON.stringify(fi.f.fieldDef)}`);
|
|
1643
1648
|
}
|
|
@@ -2791,6 +2796,8 @@ class QueryStruct extends QueryNode {
|
|
|
2791
2796
|
return new QueryFieldBoolean(field, this);
|
|
2792
2797
|
case "json":
|
|
2793
2798
|
return new QueryFieldJSON(field, this);
|
|
2799
|
+
case "unsupported":
|
|
2800
|
+
return new QueryFieldUnsupported(field, this);
|
|
2794
2801
|
// case "reduce":
|
|
2795
2802
|
// case "project":
|
|
2796
2803
|
// case "index":
|
|
@@ -241,7 +241,7 @@ declare type HasExpression = FieldDef & JustExpression;
|
|
|
241
241
|
export declare function hasExpression(f: FieldDef): f is HasExpression;
|
|
242
242
|
export declare type TimeFieldType = "date" | "timestamp";
|
|
243
243
|
export declare function isTimeFieldType(s: string): s is TimeFieldType;
|
|
244
|
-
export declare type AtomicFieldType = "string" | "number" | TimeFieldType | "boolean" | "json";
|
|
244
|
+
export declare type AtomicFieldType = "string" | "number" | TimeFieldType | "boolean" | "unsupported" | "json";
|
|
245
245
|
export declare function isAtomicFieldType(s: string): s is AtomicFieldType;
|
|
246
246
|
/** All scalars can have an optional expression */
|
|
247
247
|
export interface FieldAtomicDef extends NamedObject, Expression, ResultMetadata {
|
|
@@ -267,6 +267,11 @@ export interface FieldBooleanDef extends FieldAtomicDef {
|
|
|
267
267
|
export interface FieldJSONDef extends FieldAtomicDef {
|
|
268
268
|
type: "json";
|
|
269
269
|
}
|
|
270
|
+
/** Scalar unsupported Field */
|
|
271
|
+
export interface FieldUnsupportedDef extends FieldAtomicDef {
|
|
272
|
+
type: "unsupported";
|
|
273
|
+
rawType?: string;
|
|
274
|
+
}
|
|
270
275
|
export declare type DateUnit = "day" | "week" | "month" | "quarter" | "year";
|
|
271
276
|
export declare function isDateUnit(str: string): str is DateUnit;
|
|
272
277
|
export declare type TimestampUnit = DateUnit | "hour" | "minute" | "second";
|
|
@@ -450,7 +455,7 @@ export interface SQLBlockStructDef extends StructDef {
|
|
|
450
455
|
}
|
|
451
456
|
export declare function isSQLBlock(sd: StructDef): sd is SQLBlockStructDef;
|
|
452
457
|
/** any of the different field types */
|
|
453
|
-
export declare type FieldTypeDef = FieldStringDef | FieldDateDef | FieldTimestampDef | FieldNumberDef | FieldBooleanDef | FieldJSONDef;
|
|
458
|
+
export declare type FieldTypeDef = FieldStringDef | FieldDateDef | FieldTimestampDef | FieldNumberDef | FieldBooleanDef | FieldJSONDef | FieldUnsupportedDef;
|
|
454
459
|
export declare function isFieldTypeDef(f: FieldDef): f is FieldTypeDef;
|
|
455
460
|
export declare function isFieldTimeBased(f: FieldDef): f is FieldTimestampDef | FieldDateDef;
|
|
456
461
|
export declare function isFieldStructDef(f: FieldDef): f is StructDef;
|
|
@@ -197,7 +197,15 @@ function isTimeFieldType(s) {
|
|
|
197
197
|
}
|
|
198
198
|
exports.isTimeFieldType = isTimeFieldType;
|
|
199
199
|
function isAtomicFieldType(s) {
|
|
200
|
-
return [
|
|
200
|
+
return [
|
|
201
|
+
"string",
|
|
202
|
+
"number",
|
|
203
|
+
"date",
|
|
204
|
+
"timestamp",
|
|
205
|
+
"boolean",
|
|
206
|
+
"json",
|
|
207
|
+
"unsupported"
|
|
208
|
+
].includes(s);
|
|
201
209
|
}
|
|
202
210
|
exports.isAtomicFieldType = isAtomicFieldType;
|
|
203
211
|
// this field definition represents something in the database.
|