/// <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."); }
/// <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); }
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)); }
/// <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); }
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(); }
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))); }
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); }
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"); }
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); }
/// <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)); }
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); }
/// <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))); }
/// <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))); }
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))); }
/// <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"); }
/// <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 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"); }
/// <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); }
/// <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)); }