Skip to content
Snippets Groups Projects
Unverified Commit ff7e29cb authored by Ariya Hidayat's avatar Ariya Hidayat Committed by GitHub
Browse files

Basic type inference for custom expression (#15940)


* Make it work with MBQL instead

* Update frontend/test/metabase/lib/expressions/typeinferencer.unit.spec.js

Co-authored-by: default avatarflamber <1447303+flamber@users.noreply.github.com>
parent 9dc522a4
No related branches found
No related tags found
No related merge requests found
import { MBQL_CLAUSES } from "./config";
import { OPERATOR as OP } from "./tokenizer";
export const MONOTYPE = {
Undefined: "undefined",
Number: "number",
String: "string",
Boolean: "boolean",
};
export function infer(mbql) {
if (!Array.isArray(mbql)) {
return typeof mbql;
}
const op = mbql[0];
switch (op) {
case OP.Plus:
case OP.Minus:
case OP.Star:
case OP.Slash:
return MONOTYPE.Number;
case OP.Not:
case OP.And:
case OP.Or:
case OP.Equal:
case OP.NotEqual:
case OP.GreaterThan:
case OP.GreaterThanEqual:
case OP.LessThan:
case OP.LessThanEqual:
return MONOTYPE.Boolean;
}
if (op === "case" || op === "coalesce") {
// TODO
return MONOTYPE.Undefined;
}
const func = MBQL_CLAUSES[op];
if (func) {
const returnType = func.type;
switch (returnType) {
case "object":
return MONOTYPE.Undefined;
case "aggregation":
return MONOTYPE.Number;
default:
return returnType;
}
}
return MONOTYPE.Undefined;
}
import { compile } from "metabase/lib/expressions/compile";
import { infer } from "metabase/lib/expressions/typeinferencer";
describe("metabase/lib/expressions/typeinferencer", () => {
function resolve(kind, name) {
return [kind, name];
}
function compileAs(source, startRule) {
let mbql = null;
try {
mbql = compile({ source, startRule, resolve });
} catch (e) {}
return mbql;
}
// workaround the limitation of the parsing expecting a strict top-level grammar rule
function tryCompile(source) {
let mbql = compileAs(source, "expression");
if (!mbql) {
mbql = compileAs(source, "boolean");
}
return mbql;
}
function type(expression) {
return infer(tryCompile(expression));
}
it("should infer the type of primitives", () => {
expect(type("0")).toEqual("number");
expect(type("1")).toEqual("number");
expect(type("3.14159")).toEqual("number");
expect(type('"Hola"')).toEqual("string");
expect(type("'Bonjour!'")).toEqual("string");
});
it("should infer the result of arithmetic operations", () => {
expect(type("[Price] + [Tax]")).toEqual("number");
expect(type("1.15 * [Total]")).toEqual("number");
});
it("should infer the result of comparisons", () => {
expect(type("[Discount] > 0")).toEqual("boolean");
expect(type("[Revenue] <= [Limit] * 2")).toEqual("boolean");
expect(type("1 != 2")).toEqual("boolean");
});
it("should infer the result of logical operations", () => {
expect(type("NOT [Deal]")).toEqual("boolean");
expect(type("[A] OR [B]")).toEqual("boolean");
expect(type("[X] AND [Y]")).toEqual("boolean");
expect(type("[Rating] < 3 AND [Price] > 100")).toEqual("boolean");
});
it("should infer parenthesized subexpression", () => {
expect(type("(3.14159)")).toEqual("number");
expect(type('((((("Hola")))))')).toEqual("string");
expect(type("NOT ([Discount] > 0)")).toEqual("boolean");
});
it("should infer the result of numeric functions", () => {
expect(type("SQRT(2)")).toEqual("number");
expect(type("ABS([Latitude])")).toEqual("number");
expect(type("FLOOR([Total] / 2.45)")).toEqual("number");
});
it("should infer the result of string functions", () => {
expect(type("Ltrim([Name])")).toEqual("string");
expect(type("Concat(Upper([LastN]), [FirstN])")).toEqual("string");
expect(type("SUBSTRING([Product], 0, 3)")).toEqual("string");
expect(type("Length([Category])")).toEqual("number");
expect(type("Length([Category]) > 0")).toEqual("boolean");
});
it.skip("should infer the result of CASE", () => {
expect(type("CASE([X], 1, 2)")).toEqual("number");
expect(type("CASE([Y], 'this', 'that')")).toEqual("string");
expect(type("CASE(BigSale, Price>100, Price>200)")).toEqual("boolean");
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment