private ODataConstantExpression ParseBinaryString(string value)
        {
            if (value.Length % 2 == 0)
            {
                var result = new byte[value.Length / 2];

                for (var i = 0; i < result.Length; i++)
                {
                    if (Util.IsHex(value[i * 2]) && Util.IsHex(value[i * 2 + 1]))
                    {
                        result[i] = (byte)(Util.HexToInt(value[i * 2]) * 16 + Util.HexToInt(value[i * 2 + 1]));
                    }
                    else
                    {
                        throw new Exception(String.Format(
                                                "Binary format is invalid at {0}.", _offset
                                                ));
                    }
                }

                return(ODataExpression.Constant(result));
            }

            throw new Exception(String.Format("Binary format is invalid at {0}.", _offset));
        }
Exemple #2
0
        protected override void VisitConvert(ODataConvertExpression node)
        {
            var convertExpression = node.Expression.Type.IsImplicityCastableTo(node.Type)
                ? node.Expression
                : ODataExpression.Call(ODataFunction.Cast, new[] { node.Expression, ODataExpression.Constant(node.ClrType) });

            this.Visit(convertExpression);
        }
        private ODataConstantExpression ParceValue(string typeName, string rawValue)
        {
            var type = GetType(typeName);

            var value = TypeDescriptor.GetConverter(type).ConvertFromString(rawValue);

            return(ODataExpression.Constant(value));
        }
Exemple #4
0
        private ODataConstantExpression ParseGuidString(string value)
        {
            var match = GuidRegex.Match(value);

            if (match.Success)
            {
                if (Guid.TryParse(match.Groups[1].Value, out var guid))
                {
                    return(ODataExpression.Constant(guid));
                }
            }

            throw new FormatException($"Could not read '{value}' as Guid at {_offset}.");
        }
        private ODataConstantExpression ParseString()
        {
            var sb     = new StringBuilder();
            var hadEnd = false;

            for (_current++; _current < _source.Length; _current++)
            {
                char c = _source[_current];

                if (c == '\'')
                {
                    // Two consecutive quotes translate to a single quote in
                    // the string. This is not in the spec (2.2.2), but seems
                    // the logical thing to do (and at StackOverflow on
                    // http://stackoverflow.com/questions/3979367 they seem
                    // to think the same thing).

                    if (
                        _current < _source.Length - 1 &&
                        _source[_current + 1] == '\''
                        )
                    {
                        _current++;
                        sb.Append('\'');
                    }
                    else
                    {
                        hadEnd = true;

                        break;
                    }
                }
                else
                {
                    sb.Append(c);
                }
            }

            if (!hadEnd)
            {
                throw new Exception(String.Format(
                                        "Unterminated string starting at {0}.", _offset
                                        ));
            }

            _offset = _current + 1;

            return(ODataExpression.Constant(sb.ToString()));
        }
        private ODataConstantExpression ParseTimeString(string value)
        {
            var match = TimeSpanRegex.Match(value);

            if (match.Success)
            {
                try
                {
                    var timespan = XmlConvert.ToTimeSpan(match.Groups[1].Value);
                    return(ODataExpression.Constant(timespan));
                }
                catch
                {
                    throw new FormatException("Could not read " + value + " as TimeSpan.");
                }
            }

            throw new Exception(String.Format("Duration format is invalid at {0}.", _offset));
        }
        private ODataConstantExpression ParseGuidString(string value, bool isStrict = false)
        {
            var match = GuidRegex.IsMatch(value);

            if (match)
            {
                Guid guid;
                if (Guid.TryParse(value, out guid))
                {
                    return(ODataExpression.Constant(guid));
                }
            }
            if (isStrict)
            {
                throw new FormatException(String.Format("Could not read '{0}' as Guid at {1}.", value, _offset));
            }
            else
            {
                return(null);
            }
        }
Exemple #8
0
        private ODataExpression ParseSimple()
        {
            if (this.TryEat(ODataTokenKind.LeftParen))
            {
                // parse group
                var group = this.ParseExpression();
                this.Eat(ODataTokenKind.RightParen);
                return(group);
            }

            ODataToken next;

            if (this.TryEat(ODataTokenKind.Identifier, out next))
            {
                if (this.TryEat(ODataTokenKind.LeftParen))
                {
                    // parse function
                    ODataFunction function;
                    if (!Enum.TryParse(next.Text, ignoreCase: true, result: out function))
                    {
                        throw new ODataParseException(next.Text + " is not a known ODataFunction!");
                    }
                    var arguments = this.ParseExpressionList(this.ParseExpression, ODataTokenKind.Comma);
                    this.Eat(ODataTokenKind.RightParen);

                    if (function == ODataFunction.IsOf || function == ODataFunction.Cast)
                    {
                        var typeLiteral   = this.ReParseAsType(arguments[arguments.Count - 1]);
                        var argumentsCopy = arguments.ToArray();
                        argumentsCopy[arguments.Count - 1] = ODataExpression.Constant(typeLiteral);
                        arguments = argumentsCopy;
                    }

                    return(ODataExpression.Call(function, arguments));
                }

                // parse member access
                var type = this._elementType;                 // root element type
                ODataMemberAccessExpression access = null;    // root member
                while (true)
                {
                    // get the property
                    var property = typeof(ODataObject).IsAssignableFrom(type)
                        ? ODataEntity.GetProperty(next.Text, typeof(ODataObject))
                        : type.GetProperty(next.Text, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    if (property == null)
                    {
                        throw new ODataParseException("Property '" + next.Text + "' could not be found on type " + type.FullName);
                    }
                    access = ODataExpression.MemberAccess(access, property);

                    // if we don't see '/' followed by an identifier, we're done
                    // we can't just use TryEat() here, because we need to distinguish between Foo/Bar/* and Foo/Bar/Baz
                    if (this.Next().Kind != ODataTokenKind.Slash || this.Next(2).Kind != ODataTokenKind.Identifier)
                    {
                        break;
                    }

                    // otherwise, update next to the next id and then advance type down the property chain
                    this.Eat(ODataTokenKind.Slash);
                    next = this.Eat(ODataTokenKind.Identifier);
                    type = property.PropertyType;
                }
                return(access);
            }

            // literals
            if (this.TryEat(ODataTokenKind.NullLiteral, out next))
            {
                return(ODataExpression.Constant(null));
            }
            if (this.TryEat(ODataTokenKind.BinaryLiteral, out next))
            {
                throw new NotImplementedException("Binary Literal Parse");
            }
            if (this.TryEat(ODataTokenKind.BooleanLiteral, out next))
            {
                return(ODataExpression.Constant(bool.Parse(next.Text)));
            }
            // see comment on the enum
            //if (this.TryEat(ODataTokenKind.ByteLiteral, out next))
            //{
            //	return ODataExpression.Constant(Convert.ToByte(next.Text, fromBase: 16));
            //}
            if (this.TryEat(ODataTokenKind.DateTimeLiteral, out next))
            {
                Func <string, string> zeroIfEmpty = s => s.Length == 0 ? "0" : s;
                var dateTime = new DateTime(
                    year: int.Parse(next.Match.Groups["year"].Value),
                    month: int.Parse(next.Match.Groups["month"].Value),
                    day: int.Parse(next.Match.Groups["day"].Value),
                    hour: int.Parse(next.Match.Groups["hour"].Value),
                    minute: int.Parse(next.Match.Groups["minute"].Value),
                    second: int.Parse(zeroIfEmpty(next.Match.Groups["second"].Value)),
                    millisecond: 0
                    )
                               .AddSeconds(double.Parse(zeroIfEmpty(next.Match.Groups["fraction"].Value)));
                return(ODataExpression.Constant(dateTime));
            }
            if (this.TryEat(ODataTokenKind.DecimalLiteral, out next))
            {
                return(ODataExpression.Constant(decimal.Parse(next.Text.Substring(0, next.Text.Length - 1))));
            }
            if (this.TryEat(ODataTokenKind.DoubleLiteral, out next))
            {
                return(ODataExpression.Constant(double.Parse(next.Text)));
            }
            if (this.TryEat(ODataTokenKind.SingleLiteral, out next))
            {
                return(ODataExpression.Constant(float.Parse(next.Text.Substring(0, next.Text.Length - 1))));
            }
            if (this.TryEat(ODataTokenKind.GuidLiteral, out next))
            {
                return(ODataExpression.Constant(Guid.Parse(next.Match.Groups["digits"].Value)));
            }
            if (this.TryEat(ODataTokenKind.Int32Literal, out next))
            {
                // Note: this will fail hard if we have an out-of-range int value. However, this is consistent
                // with MSFT's implementation (see http://services.odata.org/v3/odata/odata.svc/Products?$format=json&$filter=Price%20ge%202147483648)
                return(ODataExpression.Constant(int.Parse(next.Text)));
            }
            if (this.TryEat(ODataTokenKind.Int64Literal, out next))
            {
                return(ODataExpression.Constant(long.Parse(next.Text.Substring(0, next.Text.Length - 1))));
            }
            if (this.TryEat(ODataTokenKind.StringLiteral, out next))
            {
                // unescaping, from http://stackoverflow.com/questions/3979367/how-to-escape-a-single-quote-to-be-used-in-an-odata-query
                return(ODataExpression.Constant(next.Match.Groups["chars"].Value.Replace("''", "'")));
            }

            throw new ODataParseException("Unexpected token " + this.Next());
        }
Exemple #9
0
        protected override void VisitQuery(ODataQueryExpression node)
        {
            const string RowNumberColumnName = "__medallionODataRowNumber";

            var isCounting = node.InlineCount == ODataInlineCountOption.AllPages;

            // we never do pagination when doing counting. This is because, in OData, count ignores pagination
            var hasPagination = !isCounting && (node.Skip > 0 || node.Top.HasValue);

            // we special-case "top 0" and just do WHERE 1=0 instead. This is because offset-fetch
            // does not allow a fetch of 0
            if (hasPagination && (node.Top ?? 1) == 0)
            {
                var emptyQuery = node.Update(
                    filter: ODataExpression.Constant(false),
                    top: null
                    );
                this.Visit(emptyQuery);
                return;
            }

            if (isCounting)
            {
                this.WriteLine("SELECT COUNT(1) AS theCount")
                .WriteLine("FROM (");
            }

            var hasRowNumberPagination = hasPagination &&
                                         this.syntaxProvider.Pagination == SqlSyntax.PaginationSyntax.RowNumber;

            if (hasRowNumberPagination)
            {
                this.Write("SELECT ")
                .WriteCommaDelimitedList(node.Select, ifEmpty: "*")
                .WriteLine()
                .WriteLine("FROM (");
            }

            // select
            this.Write("SELECT ");
            if (hasRowNumberPagination)
            {
                this.Write("* , ROW_NUMBER() OVER (ORDER BY ")
                .WriteCommaDelimitedList(node.OrderBy, ifEmpty: "RAND()")
                .Write(") AS ").Write(RowNumberColumnName);
            }
            else
            {
                this.WriteCommaDelimitedList(node.Select, ifEmpty: "*");
            }
            this.WriteLine();

            // from
            this.Write("FROM ").Write(this.tableSql).Write(" ").Write(Alias).WriteLine();

            // where
            if (node.Filter != null)
            {
                this.Write("WHERE ").Write(node.Filter, boolMode: BoolMode.Bool).WriteLine();
            }

            if (hasRowNumberPagination)
            {
                this.Write(") ").WriteLine(Alias); // close the subquery
                this.Write("WHERE ");
                this.syntaxProvider.RenderParameterReference(s => this.Write(s), this.CreateParameter(ODataExpression.Constant(node.Skip)));
                this.Write(" < ").Write(Alias).Write(".").Write(RowNumberColumnName);
                if (node.Top.HasValue)
                {
                    this.Write(" AND ").Write(Alias).Write(".").Write(RowNumberColumnName).Write(" <= ");
                    this.syntaxProvider.RenderParameterReference(s => this.Write(s), this.CreateParameter(ODataExpression.Constant(node.Skip + node.Top.Value)));
                }
                this.WriteLine();
            }

            // order by
            // we avoid rendering orderby when counting. We don't have to worry about pagination
            // since hasPagination is always false when counting
            if ((node.OrderBy.Count > 0 && !isCounting)
                // when doing offset-fetch pagination, we are required to have an order by clause
                || (hasPagination && this.syntaxProvider.Pagination == SqlSyntax.PaginationSyntax.OffsetFetch))
            {
                this.Write("ORDER BY ")
                .WriteCommaDelimitedList(node.OrderBy, ifEmpty: "RAND()")
                .WriteLine();
            }

            // skip/take
            if (hasPagination)
            {
                switch (this.syntaxProvider.Pagination)
                {
                case SqlSyntax.PaginationSyntax.OffsetFetch:
                    this.Write("OFFSET ");
                    this.syntaxProvider.RenderParameterReference(s => this.Write(s), this.CreateParameter(ODataExpression.Constant(node.Skip)));
                    this.WriteLine(" ROWS");
                    if (node.Top.HasValue)
                    {
                        this.Write("FETCH NEXT ");
                        this.syntaxProvider.RenderParameterReference(s => this.Write(s), this.CreateParameter(ODataExpression.Constant(node.Top.Value)));
                        this.WriteLine(" ROWS ONLY");
                    }
                    break;

                case SqlSyntax.PaginationSyntax.Limit:
                    this.Write("LIMIT ").Write(node.Skip).Write(", ")
                    .WriteLine(node.Top ?? "18446744073709551615".As <object>());
                    break;

                case SqlSyntax.PaginationSyntax.RowNumber:
                    // handled above
                    break;

                default:
                    throw Throw.UnexpectedCase(this.syntaxProvider.Pagination);
                }
            }

            if (isCounting)
            {
                this.Write(") ").WriteLine(Alias); // close the subquery
            }
        }
Exemple #10
0
        protected override void VisitCall(ODataCallExpression node)
        {
            // reference for different function syntax http://users.atw.hu/sqlnut/sqlnut2-chp-4-sect-4.html
            switch (node.Function)
            {
            case ODataFunction.Cast:
                this.Write("CAST(").Write(node.Arguments[0]);
                var toType = ((Type)((ODataConstantExpression)node.Arguments[1]).Value).ToODataExpressionType();
                this.Write(" AS ").Write(this.syntaxProvider.GetSqlTypeName(toType)).Write(")");
                break;

            case ODataFunction.Ceiling:
                this.VisitCallHelper(this.syntaxProvider.UseAbbreviatedCeilingFunction ? "CEIL" : "CEILING", node);
                break;

            case ODataFunction.Concat:
                this.Write("(").Write(node.Arguments[0])
                .Write(" + ")
                .Write(node.Arguments[1]).Write(")");
                break;

            case ODataFunction.Second:
            case ODataFunction.Minute:
            case ODataFunction.Hour:
            case ODataFunction.Day:
            case ODataFunction.Month:
            case ODataFunction.Year:
                this.syntaxProvider.RenderDatePartFunctionCall(node.Function, s => this.Write(s), () => this.Write(node.Arguments[0]));
                break;

            case ODataFunction.EndsWith:
                // endswith => (INDEXOF(needle, haystack) = LEN(haystack) - LEN(needle)) OR LEN(needle) = 0
                var needleLengthExpression = ODataExpression.Call(ODataFunction.Length, new[] { node.Arguments[1] });
                var endsWithExpression     = ODataExpression.BinaryOp(
                    ODataExpression.BinaryOp(
                        ODataExpression.Call(ODataFunction.IndexOf, node.Arguments),
                        ODataBinaryOp.Equal,
                        ODataExpression.BinaryOp(
                            ODataExpression.Call(ODataFunction.Length, new[] { node.Arguments[0] }),
                            ODataBinaryOp.Subtract,
                            needleLengthExpression
                            )
                        ),
                    ODataBinaryOp.Or,
                    ODataExpression.BinaryOp(
                        needleLengthExpression,
                        ODataBinaryOp.Equal,
                        ODataExpression.Constant(0)
                        )
                    );
                this.Visit(endsWithExpression);
                break;

            case ODataFunction.StartsWith:
                // startswith => INDEXOF(needle, haystack) = 0
                var startsWithExpression = ODataExpression.BinaryOp(
                    ODataExpression.Call(ODataFunction.IndexOf, node.Arguments),
                    ODataBinaryOp.Equal,
                    ODataExpression.Constant(0)
                    );
                this.Visit(startsWithExpression);
                break;

            case ODataFunction.SubstringOf:
                // substringof => INDEXOF(needle, haystack) >= 0
                var substringOfExpression = ODataExpression.BinaryOp(
                    ODataExpression.Call(ODataFunction.IndexOf, node.Arguments.Reverse()),
                    ODataBinaryOp.GreaterThanOrEqual,
                    ODataExpression.Constant(0)
                    );
                this.Visit(substringOfExpression);
                break;

            case ODataFunction.IndexOf:
                this.syntaxProvider.RenderIndexOfFunctionCall(s => this.Write(s), renderNeedleArgument: () => this.Write(node.Arguments[1]), renderHaystackArgument: () => this.Write(node.Arguments[0]));
                break;

            case ODataFunction.Floor:
                this.VisitCallHelper("FLOOR", node);
                break;

            case ODataFunction.Length:
                this.VisitCallHelper(this.syntaxProvider.StringLengthFunctionName, node);
                break;

            case ODataFunction.Replace:
                this.VisitCallHelper("REPLACE", node);
                break;

            case ODataFunction.Round:
                this.syntaxProvider.RenderRoundFunctionCall(s => this.Write(s), () => this.Write(node.Arguments[0]));
                break;

            case ODataFunction.Substring:
                this.syntaxProvider.RenderSubstringFunctionCall(s => this.Write(s), () => this.Write(node.Arguments[0]), () => this.Write(node.Arguments[1]), node.Arguments.Count > 2 ? new Action(() => this.Write(node.Arguments[2])) : null);
                break;

            case ODataFunction.ToLower:
                this.VisitCallHelper("LOWER", node);
                break;

            case ODataFunction.ToUpper:
                this.VisitCallHelper("UPPER", node);
                break;

            case ODataFunction.Trim:
                if (this.syntaxProvider.HasTwoSidedTrim)
                {
                    this.VisitCallHelper("TRIM", node);
                }
                else
                {
                    // call both LTRIM and RTRIM
                    this.VisitCallHelper("LTRIM(RTRIM", node);
                    this.Write(")");
                }
                break;

            case ODataFunction.IsOf:
                throw new NotSupportedException(node.Function.ToString());
            }
        }
Exemple #11
0
        private ODataExpression TranslateInternal(Expression linq)
        {
            if (linq == null)
            {
                return(null);
            }

            // captured parameters
            object value;

            if (TryGetValueFast(linq, out value))
            {
                // special case the handling of queryable constants, since these can be the "root"
                // of the query expression tree. We check for isInsideQuery since we don't allow multiple
                // roots. An example of a multiply-rooted tree would be: q.Where(x => q.Any(xx => xx.A < x.A))
                if (!this._isInsideQuery && typeof(IQueryable).GetTypeInfo().IsAssignableFrom(linq.Type.GetTypeInfo()))
                {
                    this._rootQuery = (IQueryable)value;
                    return(ODataExpression.Query());
                }
                return(ODataExpression.Constant(value, linq.Type));
            }

            switch (linq.NodeType)
            {
            case ExpressionType.OrElse:
                return(this.TranslateBinary(linq, ODataBinaryOp.Or));

            case ExpressionType.AndAlso:
                return(this.TranslateBinary(linq, ODataBinaryOp.And));

            case ExpressionType.Add:
                if (linq.Type == typeof(string))
                {
                    // special case adding strings, since that's the same as Concat
                    var binary = (BinaryExpression)linq;
                    return(this.TranslateInternal(Expression.Call(Helpers.GetMethod(() => string.Concat(default(string), default(string))), binary.Left, binary.Right)));
                }
                return(this.TranslateBinary(linq, ODataBinaryOp.Add));

            case ExpressionType.Subtract:
                return(this.TranslateBinary(linq, ODataBinaryOp.Subtract));

            case ExpressionType.Multiply:
                return(this.TranslateBinary(linq, ODataBinaryOp.Multiply));

            case ExpressionType.Divide:
                return(this.TranslateBinary(linq, ODataBinaryOp.Divide));

            case ExpressionType.Modulo:
                return(this.TranslateBinary(linq, ODataBinaryOp.Modulo));

            case ExpressionType.Equal:
                return(this.TranslateBinary(linq, ODataBinaryOp.Equal));

            case ExpressionType.NotEqual:
                return(this.TranslateBinary(linq, ODataBinaryOp.NotEqual));

            case ExpressionType.LessThan:
                return(this.TranslateBinary(linq, ODataBinaryOp.LessThan));

            case ExpressionType.LessThanOrEqual:
                return(this.TranslateBinary(linq, ODataBinaryOp.LessThanOrEqual));

            case ExpressionType.GreaterThan:
                return(this.TranslateBinary(linq, ODataBinaryOp.GreaterThan));

            case ExpressionType.GreaterThanOrEqual:
                return(this.TranslateBinary(linq, ODataBinaryOp.GreaterThanOrEqual));

            case ExpressionType.Not:
                return(ODataExpression.UnaryOp(this.TranslateInternal(((UnaryExpression)linq).Operand), ODataUnaryOp.Not));

            case ExpressionType.Negate:
                // we translate -a.B as (-1 * a.B) for numeric types
                var negated = ((UnaryExpression)linq).Operand;
                var underlyingNegatedType = Nullable.GetUnderlyingType(negated.Type) ?? negated.Type;
                if (!underlyingNegatedType.IsNumeric())
                {
                    throw new ODataCompileException("Expression '" + linq + "' could not be translated to OData: only negation of numeric types is supported");
                }
                var multiplyNegate = Expression.Multiply(
                    Expression.Constant(Convert.ChangeType(-1, underlyingNegatedType), negated.Type),
                    negated
                    );
                return(this.TranslateInternal(multiplyNegate));

            case ExpressionType.MemberAccess:
                return(this._memberAndParameterTranslator.TranslateMemberAccess((MemberExpression)linq));

            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
            case ExpressionType.TypeAs:
                return(ODataExpression.Convert(this.TranslateInternal(((UnaryExpression)linq).Operand), linq.Type));

            case ExpressionType.Parameter:
                return(this._memberAndParameterTranslator.TranslateParameter((ParameterExpression)linq));

            case ExpressionType.TypeIs:
                var typeBinary     = (TypeBinaryExpression)linq;
                var isArg          = this.TranslateInternal(typeBinary.Expression);
                var isTypeConstant = ODataExpression.Constant(typeBinary.TypeOperand);
                return(isArg != null
                                                ? ODataExpression.Call(ODataFunction.IsOf, new[] { isArg, isTypeConstant })
                                                : ODataExpression.Call(ODataFunction.IsOf, new[] { isTypeConstant }));

            case ExpressionType.Call:
                return(this.TranslateCall((MethodCallExpression)linq));

            case ExpressionType.Quote:
                var quoted = (LambdaExpression)((UnaryExpression)linq).Operand;
                if (!this._isInsideQuery)
                {
                    throw new ODataCompileException("Unexpected placement for lambda expression " + linq);
                }
                return(this.TranslateInternal(quoted.Body));

            default:
                throw new ODataCompileException("Expression '" + linq + "' of type " + linq.NodeType + " could not be translated to OData");
            }
        }
Exemple #12
0
        private ODataExpression TranslateCall(MethodCallExpression call)
        {
            // query operators
            if (call.Method.DeclaringType == typeof(Queryable))
            {
                if (this._isInsideQuery)
                {
                    throw new ODataCompileException("OData does not support nested query structures!");
                }

                // normalize overloads of query operators
                bool changedAllToAny;
                var  normalized = QueryOperatorCanonicalizer.Canonicalize(call, changedAllToAny: out changedAllToAny);
                if (normalized != call)
                {
                    // TODO FUTURE better exception message here since expression being translated won't match?
                    var result = this.TranslateInternal(normalized);
                    if (changedAllToAny)
                    {
                        var innerResultTranslator = this._resultTranslator;
                        Throw <InvalidOperationException> .If(innerResultTranslator == null, "Sanity check: expected non-null result translator");

                        this._resultTranslator = (values, count) => !((bool)innerResultTranslator(values, count));
                    }
                    return(result);
                }

                // handle "normal" query methods

                // translate the source query. If the source is a constant, that's the "root" of the query tree
                // e. g. that might be the OData equivalent of a DbSet or ObjectQuery constant
                var source = (ODataQueryExpression)this.TranslateInternal(call.Arguments[0]);
                switch (call.Method.Name)
                {
                case "Where":
                    // not the index version
                    if (call.Arguments[1].Type.IsGenericOfType(typeof(Func <, ,>)))
                    {
                        goto default;
                    }

                    var predicate = this.TranslateInsideQuery(call.Arguments[1]);
                    if (source.OrderBy.Count > 0 || source.Top.HasValue || source.Skip > 0)
                    {
                        throw new ODataCompileException("Cannot apply a filter after applying an OrderBy, Take, or Skip operation");
                    }
                    return(source.Update(filter: source.Filter != null ? ODataExpression.BinaryOp(source.Filter, ODataBinaryOp.And, predicate) : predicate));

                case "OrderBy":
                case "OrderByDescending":
                case "ThenBy":
                case "ThenByDescending":
                    if (call.Arguments.Count != 2)
                    {
                        goto default;
                    }

                    var sortKeyExpression = this.TranslateInsideQuery(call.Arguments[1]);
                    var sortKey           = ODataExpression.SortKey(sortKeyExpression, descending: call.Method.Name.EndsWith("Descending"));
                    if (source.Top.HasValue || source.Skip > 0)
                    {
                        throw new ODataCompileException("Cannot apply a sort after applying Take or Skip operations");
                    }
                    return(source.Update(
                               orderBy: call.Method.Name.StartsWith("Then")
                                                                ? source.OrderBy.Concat(sortKey.Enumerate())
                                                                : sortKey.Enumerate()
                               ));

                case "Skip":
                    object skip;
                    Throw <InvalidOperationException> .If(!TryGetValueFast(call.Arguments[1], out skip), "Could not get value");

                    if (source.Top.HasValue)
                    {
                        throw new ODataCompileException("Cannot apply a skip after applying a Take operation");
                    }
                    return(source.Update(skip: source.Skip + (int)skip));                            // not right

                case "Take":
                    object take;
                    Throw <InvalidOperationException> .If(!TryGetValueFast(call.Arguments[1], out take), "Could not get value");

                    return(Take(source, (int)take));

                case "Select":
                    /*
                     * MA: select is tricky in OData, since it doesn't just conform to the OData $select system query option.
                     * Instead, you could have an intermediate select in your query, and it would be nice to be able to support that.
                     * In that case, OData still forces us to select original columns, but we can store away the projection expressions
                     * to re-apply later. We also need to make sure to store how to map each projected property, so that we can inline these
                     * projections.
                     *
                     * For example, imagine you have the query q.Select(x => new { a = x.B + 2 }).Where(t => t.a > 5).
                     * In OData, this would translate to "$filter=B + 2 > 5&$select=B". We can then do the final projection to the anonymous
                     * type in memory on the client side
                     */

                    // we don't support select with index
                    if (!call.Arguments[1].Type.GetGenericArguments(typeof(Expression <>)).Single().IsGenericOfType(typeof(Func <,>)))
                    {
                        goto default;
                    }

                    // unquote and extract the lambda
                    var projection = ((LambdaExpression)((UnaryExpression)call.Arguments[1]).Operand);

                    // register the projection
                    this._isInsideQuery = true;
                    this._memberAndParameterTranslator.RegisterProjection(projection);
                    this._isInsideQuery = false;

                    // return the source, since the projection doesn't actually affect the returned expression
                    // until the very end when we can use it to determine which columns to $select
                    return(source);

                case "Any":
                    this._resultTranslator = MakeTranslator(call.Arguments[0], e => e.Any());
                    return(Take(source, 1));

                case "Count":
                    this._resultTranslator = (values, count) => ComputeCount(count: count.Value, skip: source.Skip, top: source.Top);
                    // top 0 is so that we don't have any wasted payload of data that we won't look at
                    return(source.Update(inlineCount: ODataInlineCountOption.AllPages, top: 0));

                case "LongCount":
                    this._resultTranslator = (values, count) => (long)ComputeCount(count: count.Value, skip: source.Skip, top: source.Top);
                    // top 0 is so that we don't have any wasted payload of data that we won't look at
                    return(source.Update(inlineCount: ODataInlineCountOption.AllPages, top: 0));

                case "First":
                    this._resultTranslator = MakeTranslator(call.Arguments[0], e => e.First());
                    return(Take(source, 1));

                case "FirstOrDefault":
                    this._resultTranslator = MakeTranslator(call.Arguments[0], e => e.FirstOrDefault());
                    return(Take(source, 1));

                case "Single":
                    this._resultTranslator = MakeTranslator(call.Arguments[0], e => e.Single());;
                    return(Take(source, 2));

                case "SingleOrDefault":
                    this._resultTranslator = MakeTranslator(call.Arguments[0], e => e.SingleOrDefault());;
                    return(Take(source, 2));

                default:
                    throw new ODataCompileException("Query operator " + call.Method + " is not supported in OData");
                }
            }

            // other OData methods

            // Enumerable/Collection contains (e. g. "IN"). This gets handled before the odata functions because
            // the thisExpression can't be translated normally. Since Contains() is declared on a bunch of different collection
            // types, we basically check that (1) there are either 2 arguments (static method) or an instance + 1 argument
            // (2) that the container argument/instance is an in-memory "constant", (3) that the collection object has an IEnumerable<T>
            // element type, and (4) that the test argument is of that element type
            // Finally, we translate the IN clause to a set of ORs
            object enumerable;
            Type   elementType;

            if (call.Method.Name == "Contains" &&
                call.Arguments.Count + Convert.ToInt32(!call.Method.IsStatic) == 2 &&
                TryGetValueFast(call.Object ?? call.Arguments[0], out enumerable) &&
                enumerable != null &&
                (elementType = enumerable.GetType().GetGenericArguments(typeof(IEnumerable <>)).SingleOrDefault()) != null &&
                call.Arguments.Last().Type == elementType)
            {
                var testExpression           = this.TranslateInternal(call.Arguments.Last());
                var equalsElementExpressions = ((IEnumerable)enumerable).Cast <object>()
                                               .Select(o => ODataExpression.Constant(o, elementType))
                                               .Select(c => ODataExpression.BinaryOp(testExpression, ODataBinaryOp.Equal, c))
                                               .ToArray();
                var equivalentOrExpression = equalsElementExpressions.Length == 0
                                        ? ODataExpression.Constant(true).As <ODataExpression>()
                                        : equalsElementExpressions.Aggregate((e1, e2) => ODataExpression.BinaryOp(e1, ODataBinaryOp.Or, e2));
                return(equivalentOrExpression);
            }

            // ODataFunctions
            var thisExpression = this.TranslateInternal(call.Object);
            var translatedArgs = call.Arguments.Select(this.TranslateInternal);

            // string functions
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "Substring")
            {
                return(ODataExpression.Call(ODataFunction.Substring, thisExpression.Enumerate().Concat(translatedArgs)));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "Replace" && call.Arguments[0].Type == typeof(string))
            {
                // for now, we don't support the char replace overload
                return(ODataExpression.Call(ODataFunction.Replace, thisExpression.Enumerate().Concat(translatedArgs)));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "Concat" && call.Arguments.All(a => a.Type == typeof(string)))
            {
                // we support only string concats, but with any fixed number of parameters
                return(translatedArgs.Aggregate((s1, s2) => ODataExpression.Call(ODataFunction.Concat, new[] { s1, s2 })));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "StartsWith" && call.Arguments.Count == 1)
            {
                return(ODataExpression.Call(ODataFunction.StartsWith, new[] { thisExpression, translatedArgs.Single() }));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "EndsWith" && call.Arguments.Count == 1)
            {
                return(ODataExpression.Call(ODataFunction.EndsWith, new[] { thisExpression, translatedArgs.Single() }));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "IndexOf" && call.Arguments.Count == 1 && call.Arguments[0].Type == typeof(string))
            {
                return(ODataExpression.Call(ODataFunction.IndexOf, new[] { thisExpression, translatedArgs.Single() }));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "Contains" && call.Arguments.Count == 1 && call.Arguments[0].Type == typeof(string))
            {
                // note: we reverse the args here because A.SubstringOf(B) is equivalent to B.Contains(A)
                return(ODataExpression.Call(ODataFunction.SubstringOf, new[] { translatedArgs.Single(), thisExpression }));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "ToLower" && call.Arguments.Count == 0)
            {
                return(ODataExpression.Call(ODataFunction.ToLower, thisExpression.Enumerate()));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "ToUpper" && call.Arguments.Count == 0)
            {
                return(ODataExpression.Call(ODataFunction.ToUpper, thisExpression.Enumerate()));
            }
            if (call.Method.DeclaringType == typeof(string) && call.Method.Name == "Trim" && call.Arguments.Count == 0)
            {
                return(ODataExpression.Call(ODataFunction.Trim, thisExpression.Enumerate()));
            }

            // math functions
            if (call.Method.DeclaringType == typeof(Math) && call.Method.Name == "Ceiling")
            {
                return(ODataExpression.Call(ODataFunction.Ceiling, translatedArgs));
            }
            if (call.Method.DeclaringType == typeof(Math) && call.Method.Name == "Floor")
            {
                return(ODataExpression.Call(ODataFunction.Floor, translatedArgs));
            }
            if (call.Method.DeclaringType == typeof(Math) && call.Method.Name == "Round" && call.Arguments.Count == 1)
            {
                return(ODataExpression.Call(ODataFunction.Round, translatedArgs));
            }

            throw new ODataCompileException("Method " + call.Method + " could not be translated to OData");
        }
        private ODataExpression ParseIdentifier(bool minus, ODataParameterExpression parameter, ODataExpression parent = null, IDictionary <string, ODataParameterExpression> lambdaParameters = null)
        {
            for (_current++; _current < _source.Length; _current++)
            {
                var c = _source[_current];

                if (!IsIdentifierChar(c))
                {
                    break;
                }
            }

            var name = _source.Substring(_offset, _current - _offset);

            var lastOffset = _offset;

            _offset = _current;

            switch (name)
            {
            case "INF":
                return(ODataExpression.Constant(double.PositiveInfinity));

            case "-INF":
                return(ODataExpression.Constant(double.NegativeInfinity));

            case "Nan":
                return(ODataExpression.Constant(double.NaN));

            case "true":
                return(ODataExpression.Constant(true));

            case "false":
                return(ODataExpression.Constant(false));

            case "null":
                return(ODataExpression.Constant(null, typeof(object)));

            case "-":
            {
                return(new ODataUnaryExpression(ExpressionType.Negate));
            }

            default:
                if (minus)
                {
                    // Reset the offset.
                    _offset = lastOffset + 1;

                    return(new ODataUnaryExpression(ExpressionType.Negate));
                }
                break;
            }

            if (_offset < _source.Length)
            {
                switch (_source[_offset])
                {
                case '\'':
                {
                    StringType stringType;

                    switch (name)
                    {
                    case "X": stringType = StringType.Binary; break;

                    case "binary": stringType = StringType.Binary; break;

                    case "datetime": stringType = StringType.DateTime; break;

                    case "guid": stringType = StringType.Guid; break;

                    case "time": stringType = StringType.Time; break;

                    case "datetimeoffset": stringType = StringType.DateTimeOffset; break;

                    default: stringType = StringType.None; break;
                    }

                    if (stringType != StringType.None && _source[_offset] == '\'')
                    {
                        var content = ParseString();

                        return(ParseSpecialString((string)content.Value, stringType));
                    }

                    if (stringType == StringType.None)
                    {
                        var content = ParseString();

                        return(ParceValue(name, (string)content.Value));
                    }

                    break;
                }

                case ':':
                {
                    _offset++;

                    var depth = 0;

                    var p = ODataExpression.Parameter(name);

                    var lp = new Dictionary <string, ODataParameterExpression>(lambdaParameters ?? new Dictionary <string, ODataParameterExpression>());

                    lp[p.Name] = p;

                    var tokens = new List <ODataExpression>();

                    while (true)
                    {
                        var token = GetNext(parameter, null, lp);

                        if (token == null)
                        {
                            break;
                        }

                        if (token.NodeType == ExpressionType.Default)
                        {
                            var syntaxExpressionToken = (ODataSyntaxExpression)token;

                            if (syntaxExpressionToken.Syntax == ',')
                            {
                                _offset--;
                                break;
                            }

                            if (syntaxExpressionToken.Syntax == '(')
                            {
                                depth++;
                            }

                            if (syntaxExpressionToken.Syntax == ')')
                            {
                                if (depth == 0)
                                {
                                    _offset--;
                                    break;
                                }

                                depth--;
                            }
                        }

                        tokens.Add(token);
                    }

                    var body = CreateExpression(ConvertToRpn(tokens));

                    var lambdaExpression = new ODataLambdaExpression
                    {
                        Parameters = new[] { p },
                        Body       = body
                    };

                    return(lambdaExpression);
                }

                case '/':
                {
                    _offset++;

                    if (lambdaParameters != null && lambdaParameters.ContainsKey(name) && parent == null)
                    {
                        return(ParseIdentifier(false, parameter, lambdaParameters[name], lambdaParameters));
                    }
                    if (name.StartsWith("Ase.") && parent != null && name.Substring(4) == parent.ToString().Replace(parameter.ToString() + ".", ""))
                    {
                        return(ParseIdentifier(false, parameter, parent, lambdaParameters));
                    }

                    return(ParseIdentifier(false, parameter, ODataExpression.PropertyOrField(name, parent ?? parameter), lambdaParameters));
                }

                case '(':    //Если следующий элемент скобка, значит это функция
                {
                    var depth     = 0;
                    var comma     = false;
                    var arguments = new List <ODataExpression>();
                    var temp      = new List <ODataExpression>();

                    while (true)
                    {
                        var token = GetNext(parameter, null, lambdaParameters);

                        if (token == null)
                        {
                            break;
                        }
                        var syntax = token as ODataSyntaxExpression;

                        if (syntax != null && syntax.Syntax == ',')
                        {
                            if (temp.Any())
                            {
                                var tokens = ConvertToRpn(temp.ToArray());

                                var expression = CreateExpression(tokens);

                                arguments.Add(expression);

                                temp = new List <ODataExpression>();

                                comma = true;
                            }
                            else
                            {
                                throw new Exception("extra comma");
                            }
                        }
                        else
                        {
                            if (syntax != null && syntax.Syntax == '(')
                            {
                                if (comma)
                                {
                                    throw new Exception("extra comma");
                                }

                                depth++;
                            }

                            if (syntax != null && syntax.Syntax == ')')
                            {
                                if (comma)
                                {
                                    throw new Exception("extra comma");
                                }

                                depth--;
                            }

                            if (syntax == null ||
                                !(syntax.Syntax == '(' && depth == 1) && !(syntax.Syntax == ')' && depth == 0))
                            {
                                temp.Add(token);
                            }

                            comma = false;

                            if (depth == 0)
                            {
                                if (temp.Any())
                                {
                                    var tokens = ConvertToRpn(temp.ToArray());

                                    var expression = CreateExpression(tokens);


                                    arguments.Add(expression);
                                }

                                break;
                            }
                        }
                    }

                    if (depth != 0)
                    {
                        throw new Exception("Parenthesis mismatch");
                    }


                    var methodCallExpression = new ODataMethodCallExpression
                    {
                        Context    = parent,
                        MethodName = name,
                        Arguments  = arguments.ToArray()
                    };

                    if (_offset < _source.Length && _source[_offset] == '/')
                    {
                        _current++;

                        _offset = _offset + 1;


                        return(ParseIdentifier(false, parameter, methodCallExpression, lambdaParameters));
                    }


                    return(methodCallExpression);
                }
                }
            }

            if (name.IsOperator())
            {
                var expressionType = name.GetExpressionType();

                if (name.IsUnaryOperator())
                {
                    return(ODataExpression.MakeUnary(expressionType, null));
                }

                if (name.IsArithmeticOperator() || name.IsLogicalOperator())
                {
                    return(ODataExpression.MakeBinary(expressionType, null, null));
                }
            }

            if (parent == null && lambdaParameters != null && lambdaParameters.ContainsKey(name))
            {
                return(lambdaParameters[name]);
            }

            if (name.Contains("."))
            {
                var type = GetType(name);
                return(ODataExpression.Constant(type));
            }

            return(ODataExpression.PropertyOrField(name, parent ?? parameter));
        }
        private ODataConstantExpression ParseNumeric()
        {
            var  floating = false;
            char c;
            var  canBeADateTime = false;

            for (_current++; _current < _source.Length; _current++)
            {
                c = _source[_current];

                if (c == '.' && !canBeADateTime)
                {
                    if (floating)
                    {
                        break;
                    }

                    floating = true;
                }
                else
                {
                    if (!Char.IsDigit(c) && c != '-' && c != 'T' && c != ':' && c != '.')
                    {
                        canBeADateTime = true;
                        break;
                    }
                }
            }

            var haveExponent = false;

            if (_current < _source.Length)
            {
                c = _source[_current];

                if (c == 'E' || c == 'e')
                {
                    _current++;

                    if (_source[_current] == '-')
                    {
                        _current++;
                    }

                    var exponentEnd = _current == _source.Length ? null : SkipDigits(_current);

                    if (!exponentEnd.HasValue)
                    {
                        throw new Exception(String.Format(
                                                "Expected digits after exponent at {0}.", _offset
                                                ));
                    }

                    _current = exponentEnd.Value;

                    haveExponent = true;

                    if (_current < _source.Length)
                    {
                        c = _source[_current];

                        if (c == 'm' || c == 'M')
                        {
                            throw new Exception(String.Format(
                                                    "Unexpected exponent for decimal literal at {0}.", _offset
                                                    ));
                        }
                        if (c == 'l' || c == 'L')
                        {
                            throw new Exception(String.Format(
                                                    "Unexpected exponent for long literal at {0}.", _offset
                                                    ));
                        }
                    }
                }
            }

            var    text = _source.Substring(_offset, _current - _offset);
            object value;


            if (_current < _source.Length)
            {
                c = _source[_current];

                switch (c)
                {
                case 'F':
                case 'f':
                    value = float.Parse(text, ParseCulture);
                    _current++;
                    break;

                case 'D':
                case 'd':
                    value = double.Parse(text, ParseCulture);
                    _current++;
                    break;

                case 'M':
                case 'm':
                    value = decimal.Parse(text, ParseCulture);
                    _current++;
                    break;

                case 'L':
                case 'l':
                    value = long.Parse(text, ParseCulture);
                    _current++;
                    break;

                case 'Z':
                    var dateTime = ParseSpecialString(text + c, StringType.DateTime);
                    _current++;
                    _offset = _current;
                    return(dateTime);

                default:
                    if (floating || haveExponent)
                    {
                        value = double.Parse(text, ParseCulture);
                    }
                    else
                    {
                        value = int.Parse(text, ParseCulture);
                    }
                    break;
                }
            }
            else
            {
                if (floating || haveExponent)
                {
                    value = double.Parse(text, ParseCulture);
                }
                else
                {
                    value = int.Parse(text, ParseCulture);
                }
            }

            _offset = _current;

            return(ODataExpression.Constant(value));
        }
        private ODataConstantExpression ParseDateTimeOffsetString(string value)
        {
            var dateTimeOffset = XmlConvert.ToDateTimeOffset(value);

            return(ODataExpression.Constant(dateTimeOffset));
        }
        private ODataConstantExpression ParseDateTimeString(string value)
        {
            var dateTime = DateTimeHelper.Parse(value);

            return(ODataExpression.Constant(dateTime));
        }