Esempio n. 1
0
        private string BindTrim(SingleValueFunctionCallQueryNode fnNode)
        {
            // SPARQL has no trim function, so we use regular expressions with replace()
            var args = fnNode.Arguments.Select(BindArgument).ToList();

            return("replace(" + args[0] + @", '^\s+|\s+$', '')"); // replace ws at start or end of string with empty string
        }
Esempio n. 2
0
        private string BindSubstringOf(SingleValueFunctionCallQueryNode fnNode)
        {
            // SPARQL equivalent is contains(str, str) with argument ordering switched
            var args = fnNode.Arguments.Select(BindArgument).ToList();

            return("contains(" + args[1] + ", " + args[0] + ")");
        }
Esempio n. 3
0
        private Expression BindTimeSpanProperty(SingleValueFunctionCallQueryNode node)
        {
            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 1 && IsDateOrOffset(arguments[0].Type));
            Contract.Assert(ClrCanonicalFunctions.TimeSpanProperties.ContainsKey(node.Name));

            return(MakeFunctionCall(ClrCanonicalFunctions.TimeSpanProperties[node.Name], arguments));
        }
Esempio n. 4
0
        private Expression BindRound(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("round" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 1 && IsDoubleOrDecimal(arguments[0].Type));

            return(MakeFunctionCall(ClrCanonicalFunctions.Round, arguments));
        }
Esempio n. 5
0
        private Expression BindSingleValueFunctionCallQueryNode(SingleValueFunctionCallQueryNode node)
        {
            switch (node.Name)
            {
            case "startswith":
                return(BindStartsWith(node));

            case "endswith":
                return(BindEndsWith(node));

            case "substringof":
                return(BindSubstringOf(node));

            case "substring":
                return(BindSubstring(node));

            case "length":
                return(BindLength(node));

            case "indexof":
                return(BindIndexOf(node));

            case "tolower":
                return(BindToLower(node));

            case "toupper":
                return(BindToUpper(node));

            case "trim":
                return(BindTrim(node));

            case "concat":
                return(BindConcat(node));

            case "year":
            case "month":
            case "day":
            case "hour":
            case "minute":
            case "second":
                return(BindDateProperty(node));

            case "round":
                return(BindRound(node));

            case "floor":
                return(BindFloor(node));

            case "ceiling":
                return(BindCeiling(node));

            default:
                throw new NotImplementedException(Error.Format(SRResources.ODataFunctionNotSupported, node.Name));
            }
        }
Esempio n. 6
0
        private Expression BindToLower(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("tolower" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);
            ValidateAllStringArguments(node.Name, arguments);

            Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));

            return(MakeFunctionCall(ClrCanonicalFunctions.ToLower, arguments));
        }
Esempio n. 7
0
        private Expression BindEndsWith(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("endswith" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);
            ValidateAllStringArguments(node.Name, arguments);

            Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));

            return(MakeFunctionCall(ClrCanonicalFunctions.EndsWith, arguments));
        }
Esempio n. 8
0
        private Expression BindLength(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("length" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));

            Expression functionCall = Expression.Property(arguments[0], "Length");

            return(CreateFunctionCallWithNullPropagation(functionCall, arguments));
        }
Esempio n. 9
0
        private Expression BindSubstringOf(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("substringof" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);
            ValidateAllStringArguments(node.Name, arguments);

            Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));

            // NOTE: this is reversed because it is reverse in WCF DS and in the OData spec
            return(MakeFunctionCall(ClrCanonicalFunctions.Contains, arguments[1], arguments[0]));
        }
Esempio n. 10
0
        private Expression BindRound(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("round" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 1 && IsDoubleOrDecimal(arguments[0].Type));

            Expression functionCall = Expression.Call(null, ClrCanonicalFunctions.Round, ExtractValueFromNullableArguments(arguments));

            return(CreateFunctionCallWithNullPropagation(functionCall, arguments));
        }
Esempio n. 11
0
        private Expression BindEndsWith(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("endswith" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));

            Expression functionCall = Expression.Call(arguments[0], ClrCanonicalFunctions.EndsWith, arguments[1]);

            return(CreateFunctionCallWithNullPropagation(functionCall, arguments));
        }
Esempio n. 12
0
        private Expression BindSubstringOf(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("substringof" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));

            // NOTE: this is reversed because it is reverse in WCF DS and in the OData spec
            Expression functionCall = Expression.Call(arguments[1], ClrCanonicalFunctions.Contains, arguments[0]);

            return(CreateFunctionCallWithNullPropagation(functionCall, arguments));
        }
Esempio n. 13
0
        private Expression BindDateProperty(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert(new string[] { "year", "month", "day", "hour", "minute", "second" }.Contains(node.Name));

            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 1 && IsDateOrOffset(arguments[0].Type));

            string propertyName = node.Name.Substring(0, 1).ToUpperInvariant() + node.Name.Substring(1);

            Expression functionCall = Expression.Property(ExtractValueFromNullableArguments(arguments)[0], propertyName);

            return(CreateFunctionCallWithNullPropagation(functionCall, arguments));
        }
Esempio n. 14
0
        private Expression BindSubstring(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("substring" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);
            if (arguments[0].Type != typeof(string))
            {
                throw new ODataException(Error.Format(SRResources.FunctionNotSupportedOnEnum, node.Name));
            }

            Expression functionCall;

            if (arguments.Length == 2)
            {
                Contract.Assert(IsInteger(arguments[1].Type));

                // When null propagation is allowed, we use a safe version of String.Substring(int).
                // But for providers that would not recognize custom expressions like this, we map
                // directly to String.Substring(int)
                if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
                {
                    // Safe function is static and takes string "this" as first argument
                    functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStartNoThrow, arguments);
                }
                else
                {
                    functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStart, arguments);
                }
            }
            else
            {
                // arguments.Length == 3 implies String.Substring(int, int)
                Contract.Assert(arguments.Length == 3 && IsInteger(arguments[1].Type) && IsInteger(arguments[2].Type));

                // When null propagation is allowed, we use a safe version of String.Substring(int, int).
                // But for providers that would not recognize custom expressions like this, we map
                // directly to String.Substring(int, int)
                if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
                {
                    // Safe function is static and takes string "this" as first argument
                    functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStartAndLengthNoThrow, arguments);
                }
                else
                {
                    functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStartAndLength, arguments);
                }
            }

            return(functionCall);
        }
Esempio n. 15
0
        private Expression BindDateOrDateTimeOffsetProperty(SingleValueFunctionCallQueryNode node)
        {
            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert(arguments.Length == 1 && IsDateOrOffset(arguments[0].Type));

            PropertyInfo property;

            if (IsDate(arguments[0].Type))
            {
                Contract.Assert(ClrCanonicalFunctions.DateProperties.ContainsKey(node.Name));
                property = ClrCanonicalFunctions.DateProperties[node.Name];
            }
            else
            {
                Contract.Assert(ClrCanonicalFunctions.DateTimeOffsetProperties.ContainsKey(node.Name));
                property = ClrCanonicalFunctions.DateTimeOffsetProperties[node.Name];
            }

            return(MakeFunctionCall(ClrCanonicalFunctions.DateProperties[node.Name], arguments));
        }
Esempio n. 16
0
        private Expression BindSubstring(SingleValueFunctionCallQueryNode node)
        {
            Contract.Assert("substring" == node.Name);

            Expression[] arguments = BindArguments(node.Arguments);

            Contract.Assert((arguments.Length == 2 && arguments[0].Type == typeof(string) && IsInteger(arguments[1].Type)) ||
                            (arguments.Length == 3 && arguments[0].Type == typeof(string) && IsInteger(arguments[1].Type) && IsInteger(arguments[2].Type)));

            Expression functionCall;

            if (arguments.Length == 2)
            {
                functionCall = Expression.Call(arguments[0], ClrCanonicalFunctions.SubstringStart, arguments[1]);
            }
            else
            {
                // arguments.Length == 3
                functionCall = Expression.Call(arguments[0], ClrCanonicalFunctions.SubstringStartAndLength, arguments[1], arguments[2]);
            }

            return(CreateFunctionCallWithNullPropagation(functionCall, arguments));
        }
Esempio n. 17
0
        private string BindSingleValueFunctionCall(SingleValueFunctionCallQueryNode fnNode)
        {
            switch (fnNode.Name.ToLowerInvariant())
            {
                case "substringof":
                    return BindSubstringOf(fnNode);
                case "endswith":
                    return BindEndsWith(fnNode);
                case "startswith":
                    return BindStartsWith(fnNode);
                case "length":
                    return BindLength(fnNode);
                case "indexof":
                    throw new NotSupportedException("SPARQL does not support an equivalent to OData indexof(str)");
                case "substring":
                    return BindSubstring(fnNode);
                case "tolower":
                    return BindToLower(fnNode);
                case "toupper":
                    return BindToUpper(fnNode);
                case "trim":
                    return BindTrim(fnNode);
                case "concat":
                    return BindConcat(fnNode);
                    
                // Date Functions
                case "day":
                    return BindDay(fnNode);
                case "hour":
                    return BindHour(fnNode);
                case "minute":
                    return BindMinute(fnNode);
                case "month":
                    return BindMonth(fnNode);
                case "second":
                    return BindSecond(fnNode);
                case "year":
                    return BindYear(fnNode);

                // Math functions
                case "round":
                    return BindRound(fnNode);
                case "ceiling":
                    return BindCeiling(fnNode);
                case "floor":
                    return BindFloor(fnNode);

                default:
                    throw new NotImplementedException("No support for function " + fnNode.Name);
            }
        }
Esempio n. 18
0
        private string BindSubstring(SingleValueFunctionCallQueryNode fnNode)
        {
            var args = fnNode.Arguments.Select(BindArgument).ToList();

            return("substr(" + String.Join(", ", args.Take(3)) + ")");
        }
Esempio n. 19
0
        private string BindConcat(SingleValueFunctionCallQueryNode fnNode)
        {
            var args = fnNode.Arguments.Select(BindArgument).ToList();

            return("concat(" + String.Join(", ", args) + ")");
        }
Esempio n. 20
0
        private string BindFloor(SingleValueFunctionCallQueryNode fnNode)
        {
            var args = fnNode.Arguments.Select(BindArgument).ToList();

            return("floor(" + args[0] + ")");
        }
Esempio n. 21
0
        private Expression BindSingleValueFunctionCallQueryNode(SingleValueFunctionCallQueryNode node)
        {
            switch (node.Name)
            {
            case ClrCanonicalFunctions.StartswithFunctionName:
                return(BindStartsWith(node));

            case ClrCanonicalFunctions.EndswithFunctionName:
                return(BindEndsWith(node));

            case ClrCanonicalFunctions.SubstringofFunctionName:
                return(BindSubstringOf(node));

            case ClrCanonicalFunctions.SubstringFunctionName:
                return(BindSubstring(node));

            case ClrCanonicalFunctions.LengthFunctionName:
                return(BindLength(node));

            case ClrCanonicalFunctions.IndexofFunctionName:
                return(BindIndexOf(node));

            case ClrCanonicalFunctions.TolowerFunctionName:
                return(BindToLower(node));

            case ClrCanonicalFunctions.ToupperFunctionName:
                return(BindToUpper(node));

            case ClrCanonicalFunctions.TrimFunctionName:
                return(BindTrim(node));

            case ClrCanonicalFunctions.ConcatFunctionName:
                return(BindConcat(node));

            case ClrCanonicalFunctions.YearFunctionName:
            case ClrCanonicalFunctions.MonthFunctionName:
            case ClrCanonicalFunctions.DayFunctionName:
            case ClrCanonicalFunctions.HourFunctionName:
            case ClrCanonicalFunctions.MinuteFunctionName:
            case ClrCanonicalFunctions.SecondFunctionName:
                return(BindDateOrDateTimeOffsetProperty(node));

            case ClrCanonicalFunctions.YearsFunctionName:
            case ClrCanonicalFunctions.MonthsFunctionName:
            case ClrCanonicalFunctions.DaysFunctionName:
            case ClrCanonicalFunctions.HoursFunctionName:
            case ClrCanonicalFunctions.MinutesFunctionName:
            case ClrCanonicalFunctions.SecondsFunctionName:
                return(BindTimeSpanProperty(node));

            case ClrCanonicalFunctions.RoundFunctionName:
                return(BindRound(node));

            case ClrCanonicalFunctions.FloorFunctionName:
                return(BindFloor(node));

            case ClrCanonicalFunctions.CeilingFunctionName:
                return(BindCeiling(node));

            default:
                throw new NotImplementedException(Error.Format(SRResources.ODataFunctionNotSupported, node.Name));
            }
        }
        /// <summary>
        /// Translates a function call which returns a single value.
        /// </summary>
        /// <param name="functionCallNode">The function call node to translate.</param>
        /// <returns>The translated expression which evaluates to the result of the function call.</returns>
        protected virtual Expression TranslateSingleValueFunctionCall(SingleValueFunctionCallQueryNode functionCallNode)
        {
            ExceptionUtils.CheckArgumentNotNull(functionCallNode, "functionCallNode");
            ExceptionUtils.CheckArgumentNotNull(functionCallNode.Name, "functionCallNode.Name");

            // First check that all arguments are single nodes and build a list so that we can go over them more than once.
            List<SingleValueQueryNode> argumentNodes = new List<SingleValueQueryNode>();
            if (functionCallNode.Arguments != null)
            {
                foreach (QueryNode argumentNode in functionCallNode.Arguments)
                {
                    SingleValueQueryNode singleValueArgumentNode = argumentNode as SingleValueQueryNode;
                    if (singleValueArgumentNode == null)
                    {
                        throw new ODataException(Strings.QueryExpressionTranslator_FunctionArgumentNotSingleValue(functionCallNode.Name));
                    }

                    argumentNodes.Add(singleValueArgumentNode);
                }
            }

            // Find the exact matching signature
            BuiltInFunctionSignature[] signatures;
            if (!BuiltInFunctions.TryGetBuiltInFunction(functionCallNode.Name, out signatures))
            {
                throw new ODataException(Strings.QueryExpressionTranslator_UnknownFunction(functionCallNode.Name));
            }

            BuiltInFunctionSignature signature =
                (BuiltInFunctionSignature)TypePromotionUtils.FindExactFunctionSignature(
                    signatures,
                    argumentNodes.Select(argumentNode => argumentNode.TypeReference).ToArray());
            if (signature == null)
            {
                throw new ODataException(
                    Strings.QueryExpressionTranslator_NoApplicableFunctionFound(
                        functionCallNode.Name,
                        BuiltInFunctions.BuildFunctionSignatureListDescription(functionCallNode.Name, signatures)));
            }

            // Translate all arguments into expressions
            Expression[] argumentExpressions = new Expression[argumentNodes.Count];
            Expression[] nonNullableArgumentExpression = new Expression[argumentNodes.Count];
            for (int argumentIndex = 0; argumentIndex < argumentNodes.Count; argumentIndex++)
            {
                SingleValueQueryNode argumentNode = argumentNodes[argumentIndex];
                Expression argumentExpression = this.Translate(argumentNode);
                argumentExpressions[argumentIndex] = argumentExpression;

                // The below code relies on the fact that all built-in functions do not take nullable parameters (in CLR space)
                // so if we do see a nullable as one of the parameters, we cast it down to non-nullable
                // If null propagation is on, we may deal with the nullable aspect later on by injecting conditionals around the function call
                // which handle the null, or if null propagation is of, passing the potential null is OK since the LINQ provider is expected
                // to handle such cases then.
                if (TypeUtils.IsNullableType(argumentExpression.Type))
                {
                    nonNullableArgumentExpression[argumentIndex] = Expression.Convert(argumentExpression, TypeUtils.GetNonNullableType(argumentExpression.Type));
                }
                else
                {
                    nonNullableArgumentExpression[argumentIndex] = argumentExpression;
                }
            }

            // Build the function call expression (as if null propagation is not required and nulls can be handled anywhere)
            Expression functionCallExpression = signature.BuildExpression(nonNullableArgumentExpression);

            // Apply the null propagation
            // Note that we inject null propagation on all arguments, which means we assume that if any of the arguments passed to function
            // is null, the entire function is null. This is true for all the built-in functions for now. If this would be to change we would have to review
            // the following code.
            if (this.nullPropagationRequired)
            {
                for (int argumentIndex = 0; argumentIndex < argumentExpressions.Length; argumentIndex++)
                {
                    Expression argumentExpression = argumentExpressions[argumentIndex];

                    // Don't null propagate the lambda parameter since it will never be null
                    //   or if the argument expression can't be null due to its type.
                    if (argumentExpression == this.parameterNodeDefinitions.Peek().Value || !TypeUtils.TypeAllowsNull(argumentExpression.Type))
                    {
                        continue;
                    }

                    // Tiny optimization: remove the check on constants which are known not to be null.
                    // Otherwise every string literal propagates out, which is correct but unnecessarily messy.
                    ConstantExpression constantExpression = argumentExpression as ConstantExpression;
                    if (constantExpression != null && constantExpression.Value != null)
                    {
                        continue;
                    }

                    Expression test = Expression.Equal(argumentExpression, Expression.Constant(null, argumentExpression.Type));
                    Expression falseBranch = functionCallExpression;
                    if (!TypeUtils.TypeAllowsNull(falseBranch.Type))
                    {
                        falseBranch = Expression.Convert(falseBranch, typeof(Nullable<>).MakeGenericType(falseBranch.Type));
                    }

                    Expression trueBranch = Expression.Constant(null, falseBranch.Type);
                    functionCallExpression = Expression.Condition(test, trueBranch, falseBranch);
                }
            }

            return functionCallExpression;
        }
Esempio n. 23
0
 private string BindFloor(SingleValueFunctionCallQueryNode fnNode)
 {
     var args = fnNode.Arguments.Select(BindArgument).ToList();
     return "floor(" + args[0] + ")";
 }
Esempio n. 24
0
 private string BindConcat(SingleValueFunctionCallQueryNode fnNode)
 {
     var args = fnNode.Arguments.Select(BindArgument).ToList();
     return "concat(" + String.Join(", ", args) + ")";
 }
Esempio n. 25
0
 private string BindTrim(SingleValueFunctionCallQueryNode fnNode)
 {
     // SPARQL has no trim function, so we use regular expressions with replace()
     var args = fnNode.Arguments.Select(BindArgument).ToList();
     return "replace(" + args[0] + @", '^\s+|\s+$', '')"; // replace ws at start or end of string with empty string
 }
Esempio n. 26
0
 private string BindSubstringOf(SingleValueFunctionCallQueryNode fnNode)
 {
     // SPARQL equivalent is contains(str, str) with argument ordering switched
     var args = fnNode.Arguments.Select(BindArgument).ToList();
     return "contains(" + args[1] + ", " + args[0] + ")";
 }
Esempio n. 27
0
 private string BindSubstring(SingleValueFunctionCallQueryNode fnNode)
 {
     var args = fnNode.Arguments.Select(BindArgument).ToList();
     return "substr(" + String.Join(", ", args.Take(3)) + ")";
 }
Esempio n. 28
0
        private string BindStartsWith(SingleValueFunctionCallQueryNode fnNode)
        {
            var args = fnNode.Arguments.Select(BindArgument).ToList();

            return("strstarts(" + args[0] + ", " + args[1] + ")");
        }
Esempio n. 29
0
 private string BindStartsWith(SingleValueFunctionCallQueryNode fnNode)
 {
     var args = fnNode.Arguments.Select(BindArgument).ToList();
     return "strstarts(" + args[0] + ", " + args[1] + ")";
 }
Esempio n. 30
0
        private string BindSingleValueFunctionCall(SingleValueFunctionCallQueryNode fnNode)
        {
            switch (fnNode.Name.ToLowerInvariant())
            {
            case "substringof":
                return(BindSubstringOf(fnNode));

            case "endswith":
                return(BindEndsWith(fnNode));

            case "startswith":
                return(BindStartsWith(fnNode));

            case "length":
                return(BindLength(fnNode));

            case "indexof":
                throw new NotSupportedException("SPARQL does not support an equivalent to OData indexof(str)");

            case "substring":
                return(BindSubstring(fnNode));

            case "tolower":
                return(BindToLower(fnNode));

            case "toupper":
                return(BindToUpper(fnNode));

            case "trim":
                return(BindTrim(fnNode));

            case "concat":
                return(BindConcat(fnNode));

            // Date Functions
            case "day":
                return(BindDay(fnNode));

            case "hour":
                return(BindHour(fnNode));

            case "minute":
                return(BindMinute(fnNode));

            case "month":
                return(BindMonth(fnNode));

            case "second":
                return(BindSecond(fnNode));

            case "year":
                return(BindYear(fnNode));

            // Math functions
            case "round":
                return(BindRound(fnNode));

            case "ceiling":
                return(BindCeiling(fnNode));

            case "floor":
                return(BindFloor(fnNode));

            default:
                throw new NotImplementedException("No support for function " + fnNode.Name);
            }
        }