// 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);
            }
        }
Esempio n. 5
0
        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));
        }
Esempio n. 7
0
        // 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);
            }
        }