// TODO: use switch expression, expression bodied member public static bool Evaluate(BooleanComparisonType op, object left, object right) { switch (op) { case BooleanComparisonType.Equals: return(left != null && left.Equals(right)); case BooleanComparisonType.NotEqualToBrackets: case BooleanComparisonType.NotEqualToExclamation: return(left == null || !left.Equals(right)); case BooleanComparisonType.GreaterThan: // TODO: handle numeric conversions properly if (left is int i && right is int j) { return(i > j); } if (left is decimal m && right is decimal n) { return(m > n); } return(string.Compare(left.ToString(), right.ToString()) > 0); case BooleanComparisonType.GreaterThanOrEqualTo: if (left is int i2 && right is int j2) { return(i2 >= j2); } if (left is decimal m2 && right is decimal n2) { return(m2 >= n2); } return(string.Compare(left.ToString(), right.ToString()) >= 0); case BooleanComparisonType.LessThan: if (left is int i3 && right is int j3) { return(i3 < j3); } if (left is decimal m3 && right is decimal n3) { return(m3 < n3); } return(string.Compare(left.ToString(), right.ToString()) < 0); case BooleanComparisonType.LessThanOrEqualTo: if (left is int i4 && right is int j4) { return(i4 <= j4); } if (left is decimal m4 && right is decimal n4) { return(m4 <= n4); } return(string.Compare(left.ToString(), right.ToString()) <= 0); default: throw FeatureNotSupportedException.Value(op); } }
private static Row CursorFetch(FetchCursorStatement fetch, Cursor cursor, Scope scope) { var orientation = fetch.FetchType?.Orientation ?? FetchOrientation.None; switch (orientation) { case FetchOrientation.None: case FetchOrientation.Next: return(cursor.MoveNext()); case FetchOrientation.Prior: return(cursor.MovePrior()); case FetchOrientation.First: return(cursor.MoveFirst()); case FetchOrientation.Last: return(cursor.MoveLast()); case FetchOrientation.Absolute: return(cursor.MoveAbsolute(Evaluate <int>(fetch.FetchType?.RowOffset, NullArgument.It, scope))); case FetchOrientation.Relative: return(cursor.MoveRelative(Evaluate <int>(fetch.FetchType?.RowOffset, NullArgument.It, scope))); default: throw FeatureNotSupportedException.Value(orientation); } }
public static object Evaluate(AssignmentKind kind, object current, object value) { // TODO: use switch expression, expression bodied member switch (kind) { case AssignmentKind.Equals: return(value); case AssignmentKind.AddEquals: // TODO: handle numeric conversions properly // TODO: and how do nulls work? if (current == null || value == null) { return(current ?? value); } if (current is int i && value is int j) { return(i + j); } return((decimal)current + (decimal)value); case AssignmentKind.SubtractEquals: return((decimal)current - (decimal)value); case AssignmentKind.MultiplyEquals: return((decimal)current * (decimal)value); case AssignmentKind.DivideEquals: return((decimal)current / (decimal)value); case AssignmentKind.ModEquals: return((decimal)current % (decimal)value); case AssignmentKind.BitwiseAndEquals: return((int)current & (int)value); case AssignmentKind.BitwiseOrEquals: return((int)current | (int)value); case AssignmentKind.BitwiseXorEquals: return((int)current ^ (int)value); default: throw FeatureNotSupportedException.Value(kind); } }
public static object Evaluate(BinaryExpressionType op, object left, object right) { // TODO: use switch expression, expression bodied member switch (op) { case BinaryExpressionType.Add: // TODO: handle numeric conversions properly if (left is string s && right is string t) { return(s + t); } if (left is int i && right is int j) { return(i + j); } return((int)left + (int)right); case BinaryExpressionType.Subtract: return((int)left - (int)right); case BinaryExpressionType.Multiply: return((int)left * (int)right); case BinaryExpressionType.Divide: return((int)left / (int)right); case BinaryExpressionType.Modulo: return((int)left % (int)right); case BinaryExpressionType.BitwiseAnd: return((int)left & (int)right); case BinaryExpressionType.BitwiseOr: return((int)left & (int)right); case BinaryExpressionType.BitwiseXor: return((int)left ^ (int)right); default: throw FeatureNotSupportedException.Value(op); } }
private static object GroupedFunctionCall(FunctionCall funCall, GroupArgument group, Scope scope) { var name = funCall.FunctionName.Value; var paramCount = funCall.Parameters.Count; var rows = group.Rows.Select(x => new RowArgument(x)); switch (name.ToLower()) { case "sum" when paramCount == 1: var expr0 = funCall.Parameters[0]; return(rows.Sum(x => Evaluate <int>(expr0, x, scope))); case "avg" when paramCount == 1: var expr1 = funCall.Parameters[0]; return(rows.Average(x => Evaluate <int>(expr1, x, scope))); case "min" when paramCount == 1: var expr2 = funCall.Parameters[0]; return(rows.Min(x => Evaluate <int>(expr2, x, scope))); case "max" when paramCount == 1: var expr3 = funCall.Parameters[0]; return(rows.Max(x => Evaluate <int>(expr3, x, scope))); case "count" when paramCount == 1: var expr4 = funCall.Parameters[0]; return(rows.Count(x => Evaluate(expr4, x, scope) != null)); case "count_big" when paramCount == 1: var expr5 = funCall.Parameters[0]; return(rows.LongCount(x => Evaluate(expr5, x, scope) != null)); default: throw FeatureNotSupportedException.Value(funCall); } }
public static EngineResult Evaluate(BinaryQueryExpressionType type, bool all, Table left, Table right, Env env) { // TODO: does offset/fetch/top happen before or after? if (left.Columns.Count != right.Columns.Count) { throw new Exception("tables must have the same number of columns"); } foreach (var i in Enumerable.Range(0, left.Columns.Count)) { var a = left.Columns[i]; var b = right.Columns[i]; if (a.Name.Length > 0 && b.Name.Length > 0 && !a.Name.Last().Similar(b.Name.Last())) { throw new Exception("columns must have the same names"); } // TODO: identify lowest common type if (a.Type != b.Type) { throw new Exception("types must match"); } } var result = new Table { Columns = left.Columns }; bool Contains(Table t, Row r) => t.Rows.Any(s => Enumerable.Range(0, r.Columns.Count).All(i => Equality.Equal(r.Values[i], s.Values[i]))); switch (type) { case BinaryQueryExpressionType.Except: foreach (var x in left.Rows) { if (!Contains(right, x) && (all || !Contains(result, x))) { result.AddCopy(x, env); } } break; case BinaryQueryExpressionType.Intersect: foreach (var x in left.Rows) { if (Contains(right, x) && (all || !Contains(result, x))) { result.AddCopy(x, env); } } if (all) { foreach (var x in right.Rows) { if (Contains(left, x)) { result.AddCopy(x, env); } } } break; case BinaryQueryExpressionType.Union: foreach (var x in left.Rows) { if (all || !Contains(result, x)) { result.AddCopy(x, env); } } foreach (var x in right.Rows) { if (all || !Contains(result, x)) { result.AddCopy(x, env); } } break; default: throw FeatureNotSupportedException.Value(type); } return(new EngineResult(result)); }
// TODO: refactor how built-in functions are implemented private static object UngroupedFunctionCall(FunctionCall funCall, IArgument arg, Scope scope) { var name = funCall.FunctionName.Value; var paramCount = funCall.Parameters.Count; // TODO: how to handle `select count(*)` ? switch (name.ToLower()) { case "error_number" when paramCount == 0: return(scope.Env.ErrorNumber); case "error_state" when paramCount == 0: return(scope.Env.ErrorState); case "error_message" when paramCount == 0: return(scope.Env.ErrorMessage); case "error_severity" when paramCount == 0: return(null); // not properly implemented case "error_line" when paramCount == 0: return(null); // not properly implemented case "error_procedure" when paramCount == 0: return(null); // not properly implemented case "isnumeric" when paramCount == 1: var value = Evaluate(funCall.Parameters[0], arg, scope); return(value != null && NumericTypes.Contains(value.GetType()) ? 1 : 0); // TODO: not supported in version 8? see unit test case "isnull" when paramCount == 2: return(Evaluate <object>(funCall.Parameters[0], arg, scope) ?? Evaluate <object>(funCall.Parameters[1], arg, scope)); case "lower" when paramCount == 1: return(Evaluate <string>(funCall.Parameters[0], arg, scope)?.ToLower()); case "upper" when paramCount == 1: return(Evaluate <string>(funCall.Parameters[0], arg, scope)?.ToUpper()); case "trim" when paramCount == 1: return(Evaluate <string>(funCall.Parameters[0], arg, scope)?.Trim()); case "ltrim" when paramCount == 1: return(Evaluate <string>(funCall.Parameters[0], arg, scope)?.TrimStart()); case "rtrim" when paramCount == 1: return(Evaluate <string>(funCall.Parameters[0], arg, scope)?.TrimEnd()); case "reverse" when paramCount == 1: var s5 = Evaluate <string>(funCall.Parameters[0], arg, scope); return(s5 == null ? null : new string(s5.Reverse().ToArray())); case "substring" when paramCount == 3: var s6 = Evaluate <string>(funCall.Parameters[0], arg, scope); var i6 = Evaluate <int>(funCall.Parameters[1], arg, scope); var j6 = Evaluate <int>(funCall.Parameters[2], arg, scope); return(s6?.Substring(i6, j6)); case "dateadd": Func <DateTime, int, DateTime> dateAdd = null; switch (funCall.Parameters[0]) { case ColumnReferenceExpression colExpr: var dateUnit = colExpr.MultiPartIdentifier.Identifiers.LastOrDefault()?.Value?.ToLower(); // TODO: break this out into another function switch (dateUnit) { case "year": case "yy": case "yyyy": dateAdd = (d, x) => d.AddYears(x); break; case "quarter": case "qq": case "q": dateAdd = (d, x) => d.AddMonths(x * 3); break; case "month": case "mm": case "m": dateAdd = (d, x) => d.AddMonths(x); break; case "week": case "wk": case "ww": dateAdd = (d, x) => d.AddDays(x * 7); break; case "dayofyear": case "dy": case "y": case "weekday": case "dw": case "w": case "day": case "dd": case "d": dateAdd = (d, x) => d.AddDays(x); break; case "hour": case "hh": dateAdd = (d, x) => d.AddHours(x); break; case "minute": case "mi": case "n": dateAdd = (d, x) => d.AddMinutes(x); break; case "second": case "ss": case "s": dateAdd = (d, x) => d.AddSeconds(x); break; case "millisecond": case "ms": dateAdd = (d, x) => d.AddMilliseconds(x); break; default: throw FeatureNotSupportedException.Value(dateUnit, "datepart"); } break; } if (dateAdd == null) { throw new Exception("invalid DATEADD() time increment argument"); } var x1 = Evaluate <int>(funCall.Parameters[1], arg, scope); var d1 = Evaluate <DateTime>(funCall.Parameters[2], arg, scope); return(dateAdd(d1, x1)); case "newid": return(Guid.NewGuid()); default: var env2 = scope.Env.Fork(); var f = scope.Env.Functions[name]; foreach (var(param, argExpr) in f.Parameters.Zip(funCall.Parameters, (param, argExpr) => (param, argExpr))) { env2.Vars.Declare(param.Key, Evaluate(argExpr, arg, scope)); } Evaluate(f.Statements, new Scope(env2)); return(env2.ReturnValue); } }