From 2a85d768a56687d7dd9f70a683185ee6928fcceb Mon Sep 17 00:00:00 2001 From: Zaid Abdulla Date: Mon, 17 Aug 2020 10:01:45 -0700 Subject: [PATCH] Implement COALESCE function --- lib/common/boolean-expression.ts | 15 ++++++++++++++- lib/common/expression-parser.ts | 2 +- lib/common/expression.ts | 24 +++++++++++++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/common/boolean-expression.ts b/lib/common/boolean-expression.ts index e6aa62e..0ec808f 100644 --- a/lib/common/boolean-expression.ts +++ b/lib/common/boolean-expression.ts @@ -586,6 +586,13 @@ function normalizeCallback(exp: Expression): Expression { if (!Array.isArray(exp)) return exp; const op = exp[0]; + if (op === "FUNC" && exp[1] === "COALESCE") { + const res: Expression[] = ["CASE"]; + for (let i = 2; i < exp.length; ++i) + res.push(normalizeCallback(["IS NOT NULL", exp[i]]), exp[i]); + return normalizeCallback(res); + } + if (op === "CASE") { const res = [] as [Expression, Expression][]; for (let i = 1; i < exp.length; i += 2) { @@ -765,7 +772,13 @@ function normalizeCallback(exp: Expression): Expression { export function normalize(expr: Expression): Expression { expr = map(expr, normalizeCallback); - if (expr instanceof Polynomial) expr = JSON.parse(expr.toString()); + if (expr instanceof Polynomial) { + expr = JSON.parse(expr.toString()); + } else if (Array.isArray(expr) && expr[0] === "CASE") { + expr = expr.map((e) => + e instanceof Polynomial ? JSON.parse(e.toString()) : e + ); + } return expr; } diff --git a/lib/common/expression-parser.ts b/lib/common/expression-parser.ts index b23e73d..07833f1 100644 --- a/lib/common/expression-parser.ts +++ b/lib/common/expression-parser.ts @@ -215,7 +215,7 @@ const lang = parsimmon.createLanguage({ .regexp(/([a-zA-Z0-9_]+)/, 1) .skip(parsimmon.optWhitespace) .desc("function"), - r.ValueExpression.sepBy( + r.Expression.sepBy( parsimmon.string(",").skip(parsimmon.optWhitespace) ).wrap( parsimmon.string("(").skip(parsimmon.optWhitespace), diff --git a/lib/common/expression.ts b/lib/common/expression.ts index 97dc82a..7b84bf8 100644 --- a/lib/common/expression.ts +++ b/lib/common/expression.ts @@ -93,6 +93,17 @@ function toNumber(a: boolean | number | string): number { } } +function toString(a: boolean | number | string): string { + switch (typeof a) { + case "string": + return a; + case "number": + return a.toString(); + case "boolean": + return (+a).toString(); + } +} + export function evaluateCallback(exp: Expression): Expression { if (!Array.isArray(exp)) return exp; if (exp[0] === "CASE") { @@ -101,6 +112,17 @@ export function evaluateCallback(exp: Expression): Expression { if (exp[i]) return exp[i + 1]; } return null; + } else if (exp[0] === "FUNC" && exp[1] === "COALESCE") { + const args: Expression[] = []; + for (let i = 2; i < exp.length; ++i) { + const e = exp[i]; + if (e == null) continue; + args.push(e); + if (!Array.isArray(e)) break; + } + if (!args.length) return null; + if (args.length === 1) return args[0]; + return ["FUNC", "COALESCE", ...args]; } else if (exp[0] === "AND") { for (let i = 1; i < exp.length; ++i) if (!Array.isArray(exp[i]) && exp[i] != null && !exp[i]) return false; @@ -223,7 +245,7 @@ export function evaluateCallback(exp: Expression): Expression { } else if (exp[0] === "||") { return reduce(exp, (a, b) => { if (a == null || b == null) return null; - if (!isArray(a) && !isArray(b)) return `${a}${b}`; + if (!isArray(a) && !isArray(b)) return toString(a) + toString(b); return REDUCE_SKIP; }); }