示例#1
0
        /// <summary>
        /// Bind this function call token as a built in function
        /// </summary>
        /// <param name="functionCallToken">the function call token to bidn</param>
        /// <param name="state">the current state of the binding algorithm</param>
        /// <param name="argumentNodes">list of semantically bound arguments</param>
        /// <returns>A function call node bound to this function.</returns>
        private static QueryNode BindAsBuiltInFunction(FunctionCallToken functionCallToken, BindingState state, List <QueryNode> argumentNodes)
        {
            if (functionCallToken.Source != null)
            {
                // the parent must be null for a built in function.
                throw new ODataException(ODataErrorStrings.FunctionCallBinder_BuiltInFunctionMustHaveHaveNullParent(functionCallToken.Name));
            }

            // There are some functions (IsOf and Cast for example) that don't necessarily need to be bound to a BuiltInFunctionSignature,
            // for these, we just Bind them directly to a SingleValueFunctionCallNode
            if (IsUnboundFunction(functionCallToken.Name))
            {
                return(CreateUnboundFunctionNode(functionCallToken, argumentNodes, state));
            }

            // Do some validation and get potential built-in functions that could match what we saw
            FunctionSignatureWithReturnType[] signatures = GetBuiltInFunctionSignatures(functionCallToken.Name);
            IEdmTypeReference[] argumentTypes            = EnsureArgumentsAreSingleValue(functionCallToken.Name, argumentNodes);

            FunctionSignatureWithReturnType signature = MatchSignatureToBuiltInFunction(functionCallToken.Name, argumentTypes, signatures);

            if (signature.ReturnType != null)
            {
                TypePromoteArguments(signature, argumentNodes);
            }

            IEdmTypeReference returnType = signature.ReturnType;

            return(new SingleValueFunctionCallNode(functionCallToken.Name, new ReadOnlyCollection <QueryNode>(argumentNodes), returnType));
        }
 public void FunctionCallQueryTokenDefaultTest()
 {
     FunctionCallToken functionCall = new FunctionCallToken("substring", null);
     this.Assert.AreEqual(QueryTokenKind.FunctionCall, functionCall.Kind, "The InternalKind property has an unexpected value.");
     this.Assert.AreEqual("substring", functionCall.Name, "The Name property has an unexpected value.");
     this.Assert.IsTrue(functionCall.Arguments != null && functionCall.Arguments.Count() == 0, "The Arguments property should NOT be null but is empty.");
 }
示例#3
0
        /// <summary>
        /// Write the function call token as URI part to this builder.
        /// </summary>
        /// <param name="functionToken">To write as URI part.</param>
        private void WriteFunctionCall(FunctionCallToken functionToken)
        {
            ExceptionUtils.CheckArgumentNotNull(functionToken, "functionToken");

            this.builder.Append(functionToken.Name);
            this.builder.Append(ExpressionConstants.SymbolOpenParen);

            bool needComma = false;

            foreach (QueryToken parameter in functionToken.Arguments)
            {
                if (needComma)
                {
                    this.builder.Append(ExpressionConstants.SymbolComma);
                }
                else
                {
                    needComma = true;
                }

                this.WriteQuery(parameter);
            }

            this.builder.Append(ExpressionConstants.SymbolClosedParen);
        }
        public override Tree <SyntaxToken> Read(LinkedList <MixedToken> tokens, Grammar grammar)
        {
            var funcNode = tokens.FindLast(t => t.IsLexicToken && t.LexicToken is FunctionCallToken);

            if (funcNode != null)
            {
                FunctionCallToken funcCallToken = (FunctionCallToken)funcNode.Value.LexicToken;
                var next = funcNode.Next;
                if (next != null)
                {
                    var nextValue = next.Value;
                    if (nextValue.IsTree)
                    {
                        UnaryStaticFunctionSyntaxToken token = new UnaryStaticFunctionSyntaxToken(funcCallToken.Method);
                        Tree <SyntaxToken>             tree  = new Tree <SyntaxToken>(token);
                        tree.Leafs.Add(next.Value.Tree);

                        tokens.AddBefore(funcNode, new MixedToken(tree));
                        tokens.Remove(funcNode);
                        tokens.Remove(next);

                        return(tree);
                    }
                }
            }
            return(null);
        }
示例#5
0
        public static AndConstraint <FunctionParameterToken> ShouldHaveParameter(this FunctionCallToken token, string name)
        {
            var argument = token.Arguments.SingleOrDefault(arg => arg.ParameterName == name);

            argument.Should().NotBeNull();
            return(new AndConstraint <FunctionParameterToken>(argument));
        }
示例#6
0
        /// <summary>
        /// Bind this function call token as a Uri function
        /// </summary>
        /// <param name="functionCallToken">the function call token to bind</param>
        /// <param name="argumentNodes">list of semantically bound arguments</param>
        /// <returns>A function call node bound to this function.</returns>
        private QueryNode BindAsUriFunction(FunctionCallToken functionCallToken, List <QueryNode> argumentNodes)
        {
            if (functionCallToken.Source != null)
            {
                // the parent must be null for a Uri function.
                throw new ODataException(ODataErrorStrings.FunctionCallBinder_UriFunctionMustHaveHaveNullParent(functionCallToken.Name));
            }

            string functionCallTokenName = this.state.Configuration.EnableCaseInsensitiveUriFunctionIdentifier ? functionCallToken.Name.ToLowerInvariant() : functionCallToken.Name;

            // There are some functions (IsOf and Cast for example) that don't necessarily need to be bound to a function signature,
            // for these, we just Bind them directly to a SingleValueFunctionCallNode
            if (IsUnboundFunction(functionCallTokenName))
            {
                return(CreateUnboundFunctionNode(functionCallTokenName, argumentNodes));
            }

            // Do some validation and get potential Uri functions that could match what we saw
            FunctionSignatureWithReturnType[] signatures = GetUriFunctionSignatures(functionCallTokenName);
            SingleValueNode[] argumentNodeArray          = ValidateArgumentsAreSingleValue(functionCallTokenName, argumentNodes);
            FunctionSignatureWithReturnType signature    = MatchSignatureToUriFunction(functionCallTokenName, argumentNodeArray, signatures);

            if (signature.ReturnType != null)
            {
                TypePromoteArguments(signature, argumentNodes);
            }

            IEdmTypeReference returnType = signature.ReturnType;

            return(new SingleValueFunctionCallNode(functionCallTokenName, new ReadOnlyCollection <QueryNode>(argumentNodes), returnType));
        }
        public static FunctionParameterToken ShouldHaveParameter(this FunctionCallToken token, string name)
        {
            Assert.NotNull(token);
            FunctionParameterToken argument = token.Arguments.SingleOrDefault(arg => arg.ParameterName == name);

            Assert.NotNull(argument);
            return(argument);
        }
示例#8
0
        public void FunctionCallQueryTokenDefaultTest()
        {
            FunctionCallToken functionCall = new FunctionCallToken("substring", null);

            this.Assert.AreEqual(QueryTokenKind.FunctionCall, functionCall.Kind, "The InternalKind property has an unexpected value.");
            this.Assert.AreEqual("substring", functionCall.Name, "The Name property has an unexpected value.");
            this.Assert.IsTrue(functionCall.Arguments != null && functionCall.Arguments.Count() == 0, "The Arguments property should NOT be null but is empty.");
        }
 public void BindFunctionForNullArgumentType()
 {
     this.functionCallBinder = new FunctionCallBinder(FakeBindMethods.BindMethodReturningANullLiteralConstantNode, this.binder.BindingState);
     var arguments = new List<QueryToken>() { new LiteralToken("ignored") };
     var token = new FunctionCallToken("year", arguments);
     var result = functionCallBinder.BindFunctionCall(token);
     result.ShouldBeSingleValueFunctionCallQueryNode("year").And.Parameters.Single().ShouldBeConstantQueryNode<object>(null).And.TypeReference.Should().BeNull();
 }
示例#10
0
        public static FunctionCallToken ShouldBeFunctionCallToken(this QueryToken token, string name)
        {
            Assert.NotNull(token);
            FunctionCallToken functionCallQueryToken = Assert.IsType <FunctionCallToken>(token);

            Assert.Equal(QueryTokenKind.FunctionCall, functionCallQueryToken.Kind);
            Assert.Equal(name, functionCallQueryToken.Name);
            return(functionCallQueryToken);
        }
 public void BindFunction()
 {
     var arguments = new List<QueryToken>() { new LiteralToken("grr"), new LiteralToken(1) };
     var token = new FunctionCallToken("substring", arguments);
     var result = functionCallBinder.BindFunctionCall(token);
     result.ShouldBeSingleValueFunctionCallQueryNode("substring");
     result.As<SingleValueFunctionCallNode>().Parameters.Count().Should().Be(2);
     result.As<SingleValueFunctionCallNode>().TypeReference.Definition.ToString().Should().Contain("String");
 }
        public void BindFunctionEmptyArguments()
        {
            // regression test for [UriParser] day() allowed. What does that mean?
            this.functionCallBinder = new FunctionCallBinder(FakeBindMethods.BindMethodReturnsNull, this.binder.BindingState);
            var token = new FunctionCallToken("day", new List<QueryToken>());
            Action bindWithEmptyArguments = () => functionCallBinder.BindFunctionCall(token);

            FunctionSignatureWithReturnType[] signatures = FunctionCallBinder.GetBuiltInFunctionSignatures("day"); // to match the error message... blah
            bindWithEmptyArguments.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    "day",
                    BuiltInFunctions.BuildFunctionSignatureListDescription("day", signatures)));
        }
示例#13
0
        public void ParsedFunctionWithAParentAndArgs()
        {
            FunctionCallParser tokenizer = GetFunctionCallParser("func(x='blah')");
            InnerPathToken     parent    = new InnerPathToken("Customer", null, null);
            QueryToken         result;
            bool success = tokenizer.TryParseIdentifierAsFunction(parent, out result);

            success.Should().BeTrue();
            FunctionCallToken functionCallToken = result.ShouldBeFunctionCallToken("func").And;

            functionCallToken.Source.Should().BeSameAs(parent);
            functionCallToken.Arguments.Should().HaveCount(1);
        }
示例#14
0
        public void ParsedFunctionWithAParentAndArgs()
        {
            FunctionCallParser tokenizer = GetFunctionCallParser("func(x='blah')");
            InnerPathToken     parent    = new InnerPathToken("Customer", null, null);
            QueryToken         result;
            bool success = tokenizer.TryParseIdentifierAsFunction(parent, out result);

            Assert.True(success);
            FunctionCallToken functionCallToken = result.ShouldBeFunctionCallToken("func");

            Assert.Same(parent, functionCallToken.Source);
            Assert.Single(functionCallToken.Arguments);
        }
        public void ParseApplyWithNestedFunctionAggregation()
        {
            string apply = "groupby((UnitPrice), aggregate(Sales($count as Count,  cast(SalesPrice, Edm.Decimal)  with average as RetailPrice)))";

            IEnumerable <QueryToken> actual = this.testSubject.ParseApply(apply);

            actual.Should().NotBeNull();
            actual.Should().HaveCount(1);

            List <QueryToken> transformations = actual.ToList();

            // verify groupby
            GroupByToken groupBy = transformations[0] as GroupByToken;

            groupBy.Should().NotBeNull();

            VerifyGroupByTokenProperties(new string[] { "UnitPrice" }, groupBy);

            groupBy.Properties.Should().HaveCount(1);
            groupBy.Child.Should().NotBeNull();

            AggregateToken groupByAggregate = groupBy.Child as AggregateToken;

            groupByAggregate.AggregateExpressions.Should().HaveCount(1);

            EntitySetAggregateToken entitySetAggregate = groupByAggregate.AggregateExpressions.First() as EntitySetAggregateToken;

            entitySetAggregate.Should().NotBeNull();

            entitySetAggregate.EntitySet.ShouldBeEndPathToken("Sales");
            entitySetAggregate.Expressions.Should().HaveCount(2);
            VerifyAggregateExpressionToken("$count", AggregationMethodDefinition.VirtualPropertyCount, "Count", entitySetAggregate.Expressions.First() as AggregateExpressionToken);

            AggregateExpressionToken funcAggregate = entitySetAggregate.Expressions.Last() as AggregateExpressionToken;

            funcAggregate.Should().NotBeNull();
            funcAggregate.Alias.ShouldBeEquivalentTo("RetailPrice");
            funcAggregate.Method.Should().Equals(AggregationMethodDefinition.Average);

            FunctionCallToken funcToken = funcAggregate.Expression as FunctionCallToken;

            funcToken.Should().NotBeNull();
            funcToken.Name.ShouldBeEquivalentTo("cast");
        }
示例#16
0
        public void ParseApplyWithNestedAggregationAndFunction()
        {
            string apply = "groupby((UnitPrice), aggregate(Sales($count as Count),  cast(SalesPrice, Edm.Decimal)  with average as RetailPrice))";

            IEnumerable <QueryToken> actual = this.testSubject.ParseApply(apply);

            Assert.NotNull(actual);
            Assert.Single(actual);

            List <QueryToken> transformations = actual.ToList();

            // verify groupby
            GroupByToken groupBy = transformations[0] as GroupByToken;

            Assert.NotNull(groupBy);

            VerifyGroupByTokenProperties(new string[] { "UnitPrice" }, groupBy);

            Assert.Single(groupBy.Properties);
            Assert.NotNull(groupBy.Child);

            AggregateToken groupByAggregate = groupBy.Child as AggregateToken;

            Assert.Equal(2, groupByAggregate.AggregateExpressions.Count());

            EntitySetAggregateToken entitySetAggregate = groupByAggregate.AggregateExpressions.First() as EntitySetAggregateToken;

            Assert.NotNull(entitySetAggregate);

            entitySetAggregate.EntitySet.ShouldBeEndPathToken("Sales");
            VerifyAggregateExpressionToken("$count", AggregationMethodDefinition.VirtualPropertyCount, "Count", entitySetAggregate.Expressions.First() as AggregateExpressionToken);

            AggregateExpressionToken funcAggregate = groupByAggregate.AggregateExpressions.Last() as AggregateExpressionToken;

            Assert.NotNull(funcAggregate);
            Assert.Equal("RetailPrice", funcAggregate.Alias);
            Assert.Equal(AggregationMethod.Average, funcAggregate.Method);

            FunctionCallToken funcToken = funcAggregate.Expression as FunctionCallToken;

            Assert.NotNull(funcToken);
            Assert.Equal("cast", funcToken.Name);
        }
        public override InputStream TryRead(InputStream stream, out LexicToken token)
        {
            token = null;

            foreach (var function in Functions)
            {
                if (stream.Content.StartsWith(function.Name))
                {
                    FunctionCallToken functionToken = new FunctionCallToken {
                        Method = function.Method
                    };
                    token = functionToken;

                    return(stream.Move(function.Name.Length));
                }
            }

            return(stream);
        }
示例#18
0
        /// <summary>
        /// Binds the token to a SingleValueFunctionCallNode
        /// </summary>
        /// <param name="functionCallToken">Token to bind</param>
        /// <returns>The resulting SingleValueFunctionCallNode</returns>
        internal QueryNode BindFunctionCall(FunctionCallToken functionCallToken)
        {
            ExceptionUtils.CheckArgumentNotNull(functionCallToken, "functionCallToken");
            ExceptionUtils.CheckArgumentNotNull(functionCallToken.Name, "functionCallToken.Name");

            // Bind the parent, if present.
            // TODO: parent can be a collection as well, so we need to loosen this to QueryNode.
            QueryNode parent = null;

            if (state.ImplicitRangeVariable != null)
            {
                if (functionCallToken.Source != null)
                {
                    parent = this.bindMethod(functionCallToken.Source);
                }
                else
                {
                    parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable);
                }
            }

            // First see if there is a custom function for this
            QueryNode boundFunction;

            if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, parent, state, out boundFunction))
            {
                return(boundFunction);
            }

            // then check if there is a global custom function(i.e with out a parent node)
            if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, null, state, out boundFunction))
            {
                return(boundFunction);
            }

            // If there isn't, bind as Uri function
            // Bind all arguments
            List <QueryNode> argumentNodes = new List <QueryNode>(functionCallToken.Arguments.Select(ar => this.bindMethod(ar)));

            return(BindAsUriFunction(functionCallToken, argumentNodes));
        }
示例#19
0
        public void ParseFilterByThisInSelectWorks()
        {
            // Arrange & Act
            SelectToken selectToken = ParseSelectClause("RelatedSSNs($filter=endswith($this,'xyz'))");

            // Assert
            Assert.NotNull(selectToken);
            SelectTermToken selectTermToken = Assert.Single(selectToken.SelectTerms);

            selectTermToken.PathToProperty.ShouldBeNonSystemToken("RelatedSSNs");
            Assert.NotNull(selectTermToken.FilterOption);

            FunctionCallToken functionCallToken = (FunctionCallToken)selectTermToken.FilterOption;

            functionCallToken.ShouldBeFunctionCallToken("endswith");
            Assert.Equal(2, functionCallToken.Arguments.Count());

            FunctionParameterToken parameterToken = functionCallToken.Arguments.First();

            parameterToken.ValueToken.ShouldBeRangeVariableToken(ExpressionConstants.This);
        }
示例#20
0
        /// <summary>
        /// Build a SingleValueFunctionCallNode for a function that isn't bound to a BuiltInFunction
        /// </summary>
        /// <param name="functionCallToken">original query token for this function</param>
        /// <param name="args">list of already bound query nodes for this function</param>
        /// <param name="state">The current state of the binding algorithm.</param>
        /// <returns>A single value function call node bound to this function.</returns>
        private static SingleValueNode CreateUnboundFunctionNode(FunctionCallToken functionCallToken, List <QueryNode> args, BindingState state)
        {
            // need to figure out the return type and check the correct number of arguments based on the function name
            IEdmTypeReference returnType = null;

            switch (functionCallToken.Name)
            {
            case ExpressionConstants.UnboundFunctionIsOf:
            {
                returnType = ValidateAndBuildIsOfArgs(state, ref args);
                break;
            }

            case ExpressionConstants.UnboundFunctionCast:
            {
                returnType = ValidateAndBuildCastArgs(state, ref args);
                if (returnType.IsEntity())
                {
                    IEdmEntityTypeReference returnEntityType = returnType.AsEntity();
                    SingleEntityNode        entityNode       = args.ElementAt(0) as SingleEntityNode;
                    if (entityNode != null)
                    {
                        return(new SingleEntityFunctionCallNode(functionCallToken.Name, args, returnEntityType, entityNode.EntitySet));
                    }
                }

                break;
            }

            default:
            {
                break;
            }
            }

            // we have everything else we need, so return the new SingleValueFunctionCallNode.
            return(new SingleValueFunctionCallNode(functionCallToken.Name, args, returnType));
        }
        public void LengthArgMustBeSingleValueNode()
        {
            QueryToken[] args = new QueryToken[]
            {
                new CustomQueryOptionToken("stuff", "stuff") 
            };

            FunctionCallToken function = new FunctionCallToken("geo.length", args);
            Action createWithNonSingleValueNode = () => this.functionCallBinder.BindFunctionCall(function);
            createWithNonSingleValueNode.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_UnsupportedQueryTokenKind("CustomQueryOption"));
        }
        public void LengthWorksWithExactlyOneArgument()
        {
            QueryToken parent = new RangeVariableToken(ExpressionConstants.It);
            QueryToken[] args = new QueryToken[]
            {
                new EndPathToken("GeometryLineString", parent),
                new LiteralToken("bob"), 
            };
            FunctionCallToken function = new FunctionCallToken("geo.length", args);
            Action createWithMoreThanOneArg = () => this.functionCallBinder.BindFunctionCall(function);

            FunctionSignatureWithReturnType[] signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(function.Name);
            createWithMoreThanOneArg.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    function.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(function.Name, signatures)));
        }
 public void TypeMustBeLastArgumentToIsOf()
 {
     QueryToken[] args = new QueryToken[]
     {
         new LiteralToken("Edm.String"), 
         new LiteralToken("stuff")
     };
     FunctionCallToken function = new FunctionCallToken("IsOf", args);
     Action createWithOutOfOrderArgs = () => this.functionCallBinder.BindFunctionCall(function);
     createWithOutOfOrderArgs.ShouldThrow<ODataException>(ODataErrorStrings.MetadataBinder_CastOrIsOfFunctionWithoutATypeArgument);
 }
 public void BindFunctionNullArgumentTypeArgumentCountMatchesFunctionSignature()
 {
     // regression test for: FunctionCallBinder should validate that the number of parameters matches for canonical function calls on open properties
     this.functionCallBinder = new FunctionCallBinder(FakeBindMethods.BindMethodReturningANullLiteralConstantNode, this.binder.BindingState);
     var arguments = new List<QueryToken>() { new LiteralToken("ignored"), new LiteralToken("ignored") };
     var token = new FunctionCallToken("day", arguments);
     Action bindWithTooManyNonTypedArgs = () => functionCallBinder.BindFunctionCall(token);
     bindWithTooManyNonTypedArgs.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload("day", "2"));
 }
        public void IntersectsMustBeCalledWithTwoArgs()
        {
            QueryToken parent = new RangeVariableToken(ExpressionConstants.It);
            QueryToken[] oneArg = new QueryToken[]
            {
                new EndPathToken("GeometryLineString", parent), 
            };

            QueryToken[] moreThanTwoArgs = new QueryToken[]
            {
                new EndPathToken("GeometryLineString", parent),
                new EndPathToken("GeographyLineString", parent),
                new EndPathToken("GeometryPolygon", parent)
            };

            FunctionCallToken functionWithOneArg = new FunctionCallToken("geo.intersects", oneArg);
            FunctionCallToken functionWithMoreThanTwoArgs = new FunctionCallToken("geo.intersects", moreThanTwoArgs);

            Action createWithOneArg = () => this.functionCallBinder.BindFunctionCall(functionWithOneArg);
            Action createWithMoreThanTwoArgs = () => this.functionCallBinder.BindFunctionCall(functionWithMoreThanTwoArgs);

            FunctionSignatureWithReturnType[] signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(functionWithOneArg.Name);
            createWithOneArg.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    functionWithOneArg.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(functionWithOneArg.Name, signatures)));

            signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(functionWithMoreThanTwoArgs.Name);
            createWithMoreThanTwoArgs.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    functionWithMoreThanTwoArgs.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(functionWithMoreThanTwoArgs.Name, signatures)));
        }
示例#26
0
 /// <summary>
 /// Binds a function call token.
 /// </summary>
 /// <param name="functionCallToken">The function call token to bind.</param>
 /// <returns>The bound function call token.</returns>
 protected virtual QueryNode BindFunctionCall(FunctionCallToken functionCallToken)
 {
     FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.Bind, this.BindingState);
     return functionCallBinder.BindFunctionCall(functionCallToken);
 }
 public void CannotCallFunctionInOpenTypeSpace()
 {
     FunctionCallToken functionCall = new FunctionCallToken(
         "Fully.Qualified.Namespace.FindMyOwner",
         new FunctionParameterToken[]
         {
             new FunctionParameterToken("dogsName", new LiteralToken("fido","'fido'"))
         },
         new EndPathToken("OpenProperty", new InnerPathToken("MyFavoritePainting", null, null)));
     Action bindOpenFunction = () => this.functionCallBinder.BindFunctionCall(functionCall);
     bindOpenFunction.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.FunctionCallBinder_CallingFunctionOnOpenProperty("Fully.Qualified.Namespace.FindMyOwner"));
 }
 public void FunctionsMustHaveEntityBindingTypes()
 {
     FunctionCallToken functionCallToken = new FunctionCallToken(
         "ChangeOwner",
         null,
         new EndPathToken("Color", new InnerPathToken("MyDog", null, null)));
     Action bindWithNonEntityBindingType = () => this.functionCallBinder.BindFunctionCall(functionCallToken);
     bindWithNonEntityBindingType.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.FunctionCallBinder_BuiltInFunctionMustHaveHaveNullParent("ChangeOwner"));
 }
 public void BuiltInFunctionsCannotHaveParentNodes()
 {
     FunctionCallToken functionCallToken = new FunctionCallToken("substring", null, new EndPathToken("Name", null));
     Action bindWithNonNullParent = () => this.functionCallBinder.BindFunctionCall(functionCallToken);
     bindWithNonNullParent.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.FunctionCallBinder_BuiltInFunctionMustHaveHaveNullParent("substring"));
 }
 public void FunctionCallBinderFindsGlobalFunctions()
 {
     FunctionCallToken functionCallToken = new FunctionCallToken(
         "Fully.Qualified.Namespace.FindMyOwner",
         new FunctionParameterToken[]
         {
             new FunctionParameterToken("dogsName", new LiteralToken("fido","'fido'"))
         },
         null);
     QueryNode functionCallNode = this.functionCallBinder.BindFunctionCall(functionCallToken);
     functionCallNode.ShouldBeSingleEntityFunctionCallNode(HardCodedTestModel.GetFunctionForFindMyOwner());
 }
        public void LengthArgMustBeLineStringType()
        {
            QueryToken parent = new RangeVariableToken(ExpressionConstants.It);
            QueryToken[] args = new QueryToken[]
            {
                new EndPathToken("GeometryLinePolygon", parent),
                new LiteralToken("bob"), 
            };
            FunctionCallToken function = new FunctionCallToken("geo.length", args);
            Action createWithNonLineStringType = () => this.functionCallBinder.BindFunctionCall(function);

            createWithNonLineStringType.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_PropertyNotDeclared("Fully.Qualified.Namespace.Person", "GeometryLinePolygon"));
        }
 public void LengthProducesSingleValueFunctionCallNode()
 {
     QueryToken parent = new RangeVariableToken(ExpressionConstants.It);
     QueryToken[] args = new QueryToken[]
     {
         new EndPathToken("GeometryLineString", parent)
     };
     FunctionCallToken function = new FunctionCallToken("geo.length", args);
     SingleValueFunctionCallNode node = this.functionCallBinder.BindFunctionCall(function) as SingleValueFunctionCallNode;
     node.Parameters.Count().Should().Be(1);
     node.Parameters.ElementAt(0).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeometryLineStringProp());
 }
 public Expression Visit(FunctionCallToken tokenIn)
 {
     throw new NotImplementedException();
 }
        public void IntersectArgsMustBeOrderedCorrectly()
        {
            //Args can be in any order, but there must be a Point and a Polygon (either geography or geometry)
            QueryToken parent = new RangeVariableToken(ExpressionConstants.It);

            //positive tests
            QueryToken[] geometryPointGeometryPoly = new QueryToken[]
            {
                new EndPathToken("GeometryPoint", parent),
                new EndPathToken("GeometryPolygon", parent) 
            };

            QueryToken[] geometryPolyGeometryPoint = new QueryToken[]
            {
                new EndPathToken("GeometryPolygon", parent),
                new EndPathToken("GeometryPoint", parent)
            };

            QueryToken[] geographyPointGeographyPoly = new QueryToken[]
            {
                new EndPathToken("GeographyPoint", parent),
                new EndPathToken("GeographyPolygon", parent) 
            };

            QueryToken[] geographyPolyGeographyPoint = new QueryToken[]
            {
                new EndPathToken("GeographyPolygon", parent),
                new EndPathToken("GeographyPoint", parent)
            };

            FunctionCallToken geometryPointGeometryPolyToken = new FunctionCallToken("geo.intersects", geometryPointGeometryPoly);
            FunctionCallToken geometryPolyGeometryPointToken = new FunctionCallToken("geo.intersects", geometryPolyGeometryPoint);
            FunctionCallToken geographyPointGeographyPolyToken = new FunctionCallToken("geo.intersects", geographyPointGeographyPoly);
            FunctionCallToken geographyPolyGeographyPointToken = new FunctionCallToken("geo.intersects", geographyPolyGeographyPoint);

            SingleValueFunctionCallNode geometryPointGeometryPolyFunction = this.functionCallBinder.BindFunctionCall(geometryPointGeometryPolyToken) as SingleValueFunctionCallNode;
            SingleValueFunctionCallNode geometryPolyGeometryPointFunction = this.functionCallBinder.BindFunctionCall(geometryPolyGeometryPointToken) as SingleValueFunctionCallNode;
            SingleValueFunctionCallNode geographyPointGeographyPolyFunction = this.functionCallBinder.BindFunctionCall(geographyPointGeographyPolyToken) as SingleValueFunctionCallNode;
            SingleValueFunctionCallNode geographyPolyGeographyPointFunction = this.functionCallBinder.BindFunctionCall(geographyPolyGeographyPointToken) as SingleValueFunctionCallNode;

            geometryPointGeometryPolyFunction.Parameters.Count().Should().Be(2);
            geometryPointGeometryPolyFunction.Parameters.ElementAt(0).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeometryPointProp());
            geometryPointGeometryPolyFunction.Parameters.ElementAt(1).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeometryPolygonProp());

            geometryPolyGeometryPointFunction.Parameters.Count().Should().Be(2);
            geometryPolyGeometryPointFunction.Parameters.ElementAt(0).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeometryPolygonProp());
            geometryPolyGeometryPointFunction.Parameters.ElementAt(1).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeometryPointProp());

            geographyPointGeographyPolyFunction.Parameters.Count().Should().Be(2);
            geographyPointGeographyPolyFunction.Parameters.ElementAt(0).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeographyPointProp());
            geographyPointGeographyPolyFunction.Parameters.ElementAt(1).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeographyPolygonProp());

            geographyPolyGeographyPointFunction.Parameters.Count().Should().Be(2);
            geographyPolyGeographyPointFunction.Parameters.ElementAt(0).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeographyPolygonProp());
            geographyPolyGeographyPointFunction.Parameters.ElementAt(1).ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonGeographyPointProp());

            //negative tests

            QueryToken[] geometryPointNonGeometryPoly = new QueryToken[]
            {
                new EndPathToken("GeometryPoint", parent),
                new LiteralToken("bob") 
            };

            QueryToken[] geometryPolyNonGeometryPoint = new QueryToken[]
            {
                new EndPathToken("GeometryPolygon", parent),
                new LiteralToken("bob") 
            };

            QueryToken[] geographyPointNonGeograpyPoly = new QueryToken[]
            {
                new EndPathToken("GeographyPoint", parent),
                new LiteralToken("bob") 
            };

            QueryToken[] geographyPolyNonGeographyPoint = new QueryToken[]
            {
                new EndPathToken("GeographyPolygon", parent),
                new LiteralToken("bob") 
            };

            QueryToken[] garbage = new QueryToken[]
            {
                new LiteralToken("tex"),
                new LiteralToken("slim") 
            };

            FunctionCallToken geometryPointNonGeometryPolyToken = new FunctionCallToken("geo.intersects", geometryPointNonGeometryPoly);
            FunctionCallToken geometryPolyNonGeometryPointToken = new FunctionCallToken("geo.intersects", geometryPolyNonGeometryPoint);
            FunctionCallToken geographyPointNonGeographyPolyToken = new FunctionCallToken("geo.intersects", geographyPointNonGeograpyPoly);
            FunctionCallToken geographyPolyNonGeographyPointToken = new FunctionCallToken("geo.intersects", geographyPolyNonGeographyPoint);
            FunctionCallToken garbageToken = new FunctionCallToken("geo.intersects", garbage);

            Action createWithGeometryPointNonGeometryPoly = () => this.functionCallBinder.BindFunctionCall(geometryPointNonGeometryPolyToken);
            Action createWithGeometryPolyNonGeometryPoint = () => this.functionCallBinder.BindFunctionCall(geometryPolyNonGeometryPointToken);
            Action createWithGeographyPointNonGeographyPoly = () => this.functionCallBinder.BindFunctionCall(geographyPointNonGeographyPolyToken);
            Action createWithGeographyPolyNonGeographyPoint = () => this.functionCallBinder.BindFunctionCall(geographyPolyNonGeographyPointToken);
            Action createWithGarbage = () => this.functionCallBinder.BindFunctionCall(garbageToken);

            FunctionSignatureWithReturnType[] signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(geometryPointNonGeometryPolyToken.Name);
            createWithGeometryPointNonGeometryPoly.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    geometryPointNonGeometryPolyToken.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(geometryPointNonGeometryPolyToken.Name, signatures)));

            signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(geometryPolyNonGeometryPointToken.Name);
            createWithGeometryPolyNonGeometryPoint.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    geometryPolyNonGeometryPointToken.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(geometryPolyNonGeometryPointToken.Name, signatures)));

            signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(geographyPointNonGeographyPolyToken.Name);
            createWithGeographyPointNonGeographyPoly.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    geographyPointNonGeographyPolyToken.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(geographyPointNonGeographyPolyToken.Name, signatures)));

            signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(geographyPolyNonGeographyPointToken.Name);
            createWithGeographyPolyNonGeographyPoint.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    geographyPolyNonGeographyPointToken.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(geographyPolyNonGeographyPointToken.Name, signatures)));


            signatures = FunctionCallBinder.GetBuiltInFunctionSignatures(garbageToken.Name);
            createWithGarbage.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    garbageToken.Name,
                    BuiltInFunctions.BuildFunctionSignatureListDescription(garbageToken.Name, signatures)));
        }
示例#35
0
 private static void VerifyFunctionCallQueryTokensAreEqual(FunctionCallToken expected, FunctionCallToken actual, AssertionHandler assert)
 {
     assert.AreEqual(expected.Name, actual.Name, "The Name of the function call token doesn't match the expected one.");
     VerifyQueryTokensAreEqual(expected.Arguments, actual.Arguments, assert);
 }
        public void CannotBindArbitraryNumberOfOpenParametersWithCorrectNonOpenParameters()
        {
            this.functionCallBinder = new FunctionCallBinder(binder.Bind, this.binder.BindingState);
            var arguments = new List<QueryToken>() { new LiteralToken("datetime'1996-02-04'"), new LiteralToken("ignored") };
            var token = new FunctionCallToken("day", arguments);
            Action bindWithEmptyArguments = () => functionCallBinder.BindFunctionCall(token);

            FunctionSignatureWithReturnType[] signatures = FunctionCallBinder.GetBuiltInFunctionSignatures("day"); // to match the error message... blah
            bindWithEmptyArguments.ShouldThrow<ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                    "day",
                    BuiltInFunctions.BuildFunctionSignatureListDescription("day", signatures)));
        }
示例#37
0
        /// <summary>
        /// Bind this function call token as a built in function
        /// </summary>
        /// <param name="functionCallToken">the function call token to bind</param>
        /// <param name="argumentNodes">list of semantically bound arguments</param>
        /// <returns>A function call node bound to this function.</returns>
        private QueryNode BindAsBuiltInFunction(FunctionCallToken functionCallToken, List<QueryNode> argumentNodes)
        {
            if (functionCallToken.Source != null)
            {
                // the parent must be null for a built in function.
                throw new ODataException(ODataErrorStrings.FunctionCallBinder_BuiltInFunctionMustHaveHaveNullParent(functionCallToken.Name));
            }

            string functionCallTokenName = this.state.Configuration.EnableCaseInsensitiveBuiltinIdentifier ? functionCallToken.Name.ToLowerInvariant() : functionCallToken.Name;

            // There are some functions (IsOf and Cast for example) that don't necessarily need to be bound to a BuiltInFunctionSignature,
            // for these, we just Bind them directly to a SingleValueFunctionCallNode
            if (IsUnboundFunction(functionCallTokenName))
            {
                return CreateUnboundFunctionNode(functionCallTokenName, argumentNodes);
            }

            // Do some validation and get potential built-in functions that could match what we saw
            FunctionSignatureWithReturnType[] signatures = GetBuiltInFunctionSignatures(functionCallTokenName);
            SingleValueNode[] argumentNodeArray = ValidateArgumentsAreSingleValue(functionCallTokenName, argumentNodes);
            FunctionSignatureWithReturnType signature = MatchSignatureToBuiltInFunction(functionCallTokenName, argumentNodeArray, signatures);
            if (signature.ReturnType != null)
            {
                TypePromoteArguments(signature, argumentNodes);
            }

            IEdmTypeReference returnType = signature.ReturnType;

            return new SingleValueFunctionCallNode(functionCallTokenName, new ReadOnlyCollection<QueryNode>(argumentNodes), returnType);
        }
 public void IsOfFunctionBindReturnsWorksWithTwoArguments()
 {
     QueryToken[] args = new QueryToken[]
     {
         new LiteralToken("stuff"),
         new LiteralToken("Edm.String"),  
     };
     FunctionCallToken functionToken = new FunctionCallToken("isof", args);
     QueryNode node = this.functionCallBinder.BindFunctionCall(functionToken);
     node.ShouldBeSingleValueFunctionCallQueryNode("isof");
     node.As<SingleValueFunctionCallNode>().Parameters.ElementAt(0).ShouldBeConstantQueryNode("stuff");
     node.As<SingleValueFunctionCallNode>().Parameters.ElementAt(1).ShouldBeConstantQueryNode("Edm.String");
 }
示例#39
0
        /// <summary>
        /// Binds a function call token.
        /// </summary>
        /// <param name="functionCallToken">The function call token to bind.</param>
        /// <returns>The bound function call token.</returns>
        protected virtual QueryNode BindFunctionCall(FunctionCallToken functionCallToken)
        {
            FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.Bind, this.BindingState);

            return(functionCallBinder.BindFunctionCall(functionCallToken));
        }
示例#40
0
 private static void VerifyFunctionCallQueryTokensAreEqual(FunctionCallToken expected, FunctionCallToken actual, AssertionHandler assert)
 {
     assert.AreEqual(expected.Name, actual.Name, "The Name of the function call token doesn't match the expected one.");
     VerifyQueryTokensAreEqual(expected.Arguments, actual.Arguments, assert);
 }
 public void IsOfFunctionBindReturnsWorksWithOneArgument()
 {
     QueryToken[] args = new QueryToken[]
     {
         new LiteralToken("Edm.String"),  
     };
     FunctionCallToken functionToken = new FunctionCallToken("isof", args);
     QueryNode node = this.functionCallBinder.BindFunctionCall(functionToken);
     node.As<SingleValueFunctionCallNode>().Parameters.ElementAt(0).ShouldBeEntityRangeVariableReferenceNode(ExpressionConstants.It);
     node.ShouldBeSingleValueFunctionCallQueryNode("isof");
     node.As<SingleValueFunctionCallNode>().Parameters.ElementAt(1).ShouldBeConstantQueryNode("Edm.String");
 }
示例#42
0
 /// <summary>
 /// Visits a FunctionCallToken
 /// </summary>
 /// <param name="tokenIn">The FunctionCallToken to visit</param>
 /// <returns>A SingleValueFunctionCallNode bound to this FunctionCallToken</returns>
 public virtual T Visit(FunctionCallToken tokenIn)
 {
     throw new NotImplementedException();
 }
        public void IsOfFunctionMustHaveExactlyTwoArguments()
        {
            QueryToken[] moreThanTwoArgs = new QueryToken[]
            {
                new LiteralToken("stuff"),
                new LiteralToken("more stuff"),
                new LiteralToken("Edm.String"),  
            };

            FunctionCallToken functionWithMoreThanTwoArgs = new FunctionCallToken("isof", moreThanTwoArgs);

            Action createWithMoreThanTwoArgs = () => this.functionCallBinder.BindFunctionCall(functionWithMoreThanTwoArgs);

            createWithMoreThanTwoArgs.ShouldThrow<ODataException>(ODataErrorStrings.MetadataBinder_CastOrIsOfExpressionWithWrongNumberOfOperands(3));
        }
 public void IsOfMustHaveATypeArgument()
 {
     QueryToken[] args = new QueryToken[]
     {
         new LiteralToken("stuff"), 
     };
     FunctionCallToken function = new FunctionCallToken("cast", args);
     Action createWithoutATypeArg = () => this.functionCallBinder.BindFunctionCall(function);
     createWithoutATypeArg.ShouldThrow<ODataException>(ODataErrorStrings.MetadataBinder_CastOrIsOfFunctionWithoutATypeArgument);
 }
示例#45
0
        /// <summary>
        /// Binds the token to a SingleValueFunctionCallNode
        /// </summary>
        /// <param name="functionCallToken">Token to bind</param>
        /// <returns>The resulting SingleValueFunctionCallNode</returns>
        internal QueryNode BindFunctionCall(FunctionCallToken functionCallToken)
        {
            ExceptionUtils.CheckArgumentNotNull(functionCallToken, "functionCallToken");
            ExceptionUtils.CheckArgumentNotNull(functionCallToken.Name, "functionCallToken.Name");

            // Bind the parent, if present.
            // TODO: parent can be a collection as well, so we need to loosen this to QueryNode.
            QueryNode parent = null;
            if (state.ImplicitRangeVariable != null)
            {
                if (functionCallToken.Source != null)
                {
                    parent = this.bindMethod(functionCallToken.Source);
                }
                else
                {
                    parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable);
                }
            }

            // First see if there is a custom function for this
            QueryNode boundFunction;
            if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, parent, state, out boundFunction))
            {
                return boundFunction;
            }

            // then check if there is a global custom function(i.e with out a parent node)
            if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, null, state, out boundFunction))
            {
                return boundFunction;
            }

            // If there isn't, bind as built-in function
            // Bind all arguments
            List<QueryNode> argumentNodes = new List<QueryNode>(functionCallToken.Arguments.Select(ar => this.bindMethod(ar)));
            return BindAsBuiltInFunction(functionCallToken, argumentNodes);
        }
 public void CastReturnsAnEntityNodeWhenTheReturnTypeIsAnEntity()
 {
     QueryToken[] args = new QueryToken[]
     {
         new EndPathToken("MyDog", null),
         new LiteralToken("Fully.Qualified.Namespace.Dog")
     };
     FunctionCallToken function = new FunctionCallToken("cast", args);
     SingleEntityFunctionCallNode functionCallNode = this.functionCallBinder.BindFunctionCall(function) as SingleEntityFunctionCallNode;
     functionCallNode.Should().NotBeNull();
     functionCallNode.Parameters.Should().HaveCount(2);
     functionCallNode.Parameters.ElementAt(0).ShouldBeSingleNavigationNode(HardCodedTestModel.GetPersonMyDogNavProp());
     functionCallNode.Parameters.ElementAt(1).ShouldBeConstantQueryNode("Fully.Qualified.Namespace.Dog");
 }
 public bool Visit(FunctionCallToken tokenIn)
 {
     throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "QueryToken of type '{0}' is not supported.", tokenIn.Kind));
 }