diff --git a/examples/simple/simple.csproj b/examples/simple/simple.csproj index 134e583..e2a4b1e 100644 --- a/examples/simple/simple.csproj +++ b/examples/simple/simple.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/src/Common/Common.csproj b/src/Common/Common.csproj index e50775f..3dbb10a 100644 --- a/src/Common/Common.csproj +++ b/src/Common/Common.csproj @@ -18,7 +18,7 @@ openCypher transpiler graph C# .NET git https://github.com/microsoft/openCypherTranspiler/tree/master/src/openCypherParser - 0.1.0 + 0.1.1 diff --git a/src/Common/Utils/TypeHelper.cs b/src/Common/Utils/TypeHelper.cs index 856e0f0..5671523 100644 --- a/src/Common/Utils/TypeHelper.cs +++ b/src/Common/Utils/TypeHelper.cs @@ -14,7 +14,7 @@ namespace openCypherTranspiler.Common.Utils /// /// Check if null value can be assigned /// E.g. IsNullableType(string) = true, IsNullableType(DateTime) = false - /// IsNullableType( + /// IsNullableType(System.Nullable) = true /// /// /// diff --git a/src/LogicalPlanner/LogicalPlanner.csproj b/src/LogicalPlanner/LogicalPlanner.csproj index 3f567f5..df4b874 100644 --- a/src/LogicalPlanner/LogicalPlanner.csproj +++ b/src/LogicalPlanner/LogicalPlanner.csproj @@ -18,7 +18,7 @@ openCypher transpiler graph C# .NET git https://github.com/microsoft/openCypherTranspiler/tree/master/src/LogicalPlanner - 0.1.0 + 0.1.1 diff --git a/src/SQLRenderer/SQLRenderer.csproj b/src/SQLRenderer/SQLRenderer.csproj index a8e5c90..9df88cb 100644 --- a/src/SQLRenderer/SQLRenderer.csproj +++ b/src/SQLRenderer/SQLRenderer.csproj @@ -18,7 +18,7 @@ openCypher transpiler graph C# .NET git https://github.com/microsoft/openCypherTranspiler/tree/master/src/openCypherParser - 0.1.0 + 0.1.1 diff --git a/src/openCypherParser/AST/Expressions/QueryExpressionFunction.cs b/src/openCypherParser/AST/Expressions/QueryExpressionFunction.cs index 64f6403..86ce020 100644 --- a/src/openCypherParser/AST/Expressions/QueryExpressionFunction.cs +++ b/src/openCypherParser/AST/Expressions/QueryExpressionFunction.cs @@ -39,23 +39,23 @@ namespace openCypherTranspiler.openCypherParser.AST public override Type EvaluateType() { var innerType = InnerExpression.EvaluateType(); - var isWrappedinNullable = TypeHelper.IsSystemNullableType(innerType); + var canBeNull = TypeHelper.CanAssignNullToType(innerType); switch (Function.FunctionName) { case Common.Function.ToFloat: - return isWrappedinNullable ? typeof(float?) : typeof(float); + return canBeNull ? typeof(float?) : typeof(float); case Common.Function.ToString: return typeof(string); case Common.Function.ToBoolean: - return isWrappedinNullable ? typeof(bool?) : typeof(bool); + return canBeNull ? typeof(bool?) : typeof(bool); case Common.Function.ToInteger: - return isWrappedinNullable ? typeof(int?) : typeof(int); + return canBeNull ? typeof(int?) : typeof(int); case Common.Function.ToDouble: - return isWrappedinNullable ? typeof(long?) : typeof(long); + return canBeNull ? typeof(long?) : typeof(long); case Common.Function.ToLong: - return isWrappedinNullable ? typeof(double?) : typeof(double); + return canBeNull ? typeof(double?) : typeof(double); case Common.Function.Not: - return isWrappedinNullable ? typeof(bool?) : typeof(bool); + return canBeNull ? typeof(bool?) : typeof(bool); case Common.Function.StringContains: case Common.Function.StringStartsWith: case Common.Function.StringEndsWith: diff --git a/src/openCypherParser/Common/Function.cs b/src/openCypherParser/Common/Function.cs index e2558c5..c87c136 100644 --- a/src/openCypherParser/Common/Function.cs +++ b/src/openCypherParser/Common/Function.cs @@ -82,6 +82,26 @@ namespace openCypherTranspiler.openCypherParser.Common } } + private static void EnsureStringType(FunctionInfo info, Type type) + { + if (!( + type == typeof(string) + )) + { + throw new TranspilerNotSupportedException($"Function {info.FunctionName} parameter of type {type.Name}"); + } + } + + private static void EnsureLengthType(FunctionInfo info, Type type) + { + if (!( + type == typeof(int) || type == typeof(long) + )) + { + throw new TranspilerNotSupportedException($"Function {info.FunctionName} parameter of type {type.Name}"); + } + } + private static void EnsureBooleanType(FunctionInfo info, Type type) { if (!( @@ -188,7 +208,11 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringStartsWith, RequiredParameters = 2, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + EnsureStringType(info, types.Skip(1).First()); + } } }, { "stringendswith", new FunctionInfo() @@ -196,7 +220,11 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringEndsWith, RequiredParameters = 2, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + EnsureStringType(info, types.Skip(1).First()); + } } }, { "stringcontains", new FunctionInfo() @@ -204,7 +232,11 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringContains, RequiredParameters = 2, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + EnsureStringType(info, types.Skip(1).First()); + } } }, { "isnull", new FunctionInfo() @@ -228,7 +260,11 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringLeft, RequiredParameters = 2, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + EnsureLengthType(info, types.Skip(1).First()); + } } }, { "right", new FunctionInfo() @@ -236,7 +272,11 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringRight, RequiredParameters = 2, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + EnsureLengthType(info, types.Skip(1).First()); + } } }, { "trim", new FunctionInfo() @@ -244,7 +284,10 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringTrim, RequiredParameters = 1, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + } } }, { "ltrim", new FunctionInfo() @@ -252,7 +295,10 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringLTrim, RequiredParameters = 1, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + } } }, { "rtrim", new FunctionInfo() @@ -260,7 +306,10 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringRTrim, RequiredParameters = 1, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + } } }, { "toupper", new FunctionInfo() @@ -268,7 +317,10 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringToUpper, RequiredParameters = 1, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + } } }, { "tolower", new FunctionInfo() @@ -276,7 +328,10 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringToLower, RequiredParameters = 1, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + } } }, { "size", new FunctionInfo() @@ -284,7 +339,10 @@ namespace openCypherTranspiler.openCypherParser.Common FunctionName = Function.StringSize, RequiredParameters = 1, OptionalParameters = 0, - ParameterChecker = EnsureParameterCountChecker + ParameterChecker = (info, types) => { + EnsureParameterCount(info, types.Count()); + EnsureStringType(info, types.First()); + } } }, }; diff --git a/src/openCypherParser/openCypherParser.csproj b/src/openCypherParser/openCypherParser.csproj index a51d3ab..93d81d8 100644 --- a/src/openCypherParser/openCypherParser.csproj +++ b/src/openCypherParser/openCypherParser.csproj @@ -18,7 +18,7 @@ openCypher transpiler graph C# .NET git https://github.com/microsoft/openCypherTranspiler/tree/master/src/openCypherParser - 0.1.0 + 0.1.1 CS3021 diff --git a/tests/LogicalPlanner.Test/LogicalPlannerTest.cs b/tests/LogicalPlanner.Test/LogicalPlannerTest.cs index 155169b..b97164f 100644 --- a/tests/LogicalPlanner.Test/LogicalPlannerTest.cs +++ b/tests/LogicalPlanner.Test/LogicalPlannerTest.cs @@ -13,6 +13,7 @@ using openCypherTranspiler.openCypherParser; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; using JT = openCypherTranspiler.LogicalPlanner.JoinOperator.JoinType; +using System; namespace openCypherTranspiler.LogicalPlanner.Test { @@ -352,6 +353,30 @@ ORDER BY p.Name LIMIT 3 } } + [TestMethod] + public void TypeEvaluationTest() + { + IGraphSchemaProvider graphDef = new JSONGraphSchema(@"./TestData/MovieGraph.json"); + // Basic test covers type coercion and type evaluation + { + var lp = RunQueryAndDumpTree(graphDef, @" +MATCH (p:Person)-[a:ACTED_IN]->(m:Movie) +WITH toString(m.Released) as ReleasedStr, m.Released as ReleasedInt +RETURN toInteger(ReleasedStr) as Released, toFloat(ReleasedStr) as ReleasedFloat, toFloat(ReleasedStr)/10.0 as ReleasedDouble, toBoolean(""true"") as ReleasedBool, toFloat(ReleasedStr)+ReleasedInt as ReleasedFloat2 +" + ); + + Assert.IsTrue(lp.StartingOperators.Count() > 0); + Assert.IsTrue(lp.TerminalOperators.Count() == 1); + Assert.IsTrue(lp.TerminalOperators.First().OutputSchema.Count == 5); + Assert.IsTrue(lp.TerminalOperators.First().OutputSchema.Any(o => o.FieldAlias == "Released" && ((o as ValueField)?.FieldType ?? default(Type)) == typeof(int?))); + Assert.IsTrue(lp.TerminalOperators.First().OutputSchema.Any(o => o.FieldAlias == "ReleasedFloat" && ((o as ValueField)?.FieldType ?? default(Type)) == typeof(float?))); + Assert.IsTrue(lp.TerminalOperators.First().OutputSchema.Any(o => o.FieldAlias == "ReleasedDouble" && ((o as ValueField)?.FieldType ?? default(Type)) == typeof(double?))); + Assert.IsTrue(lp.TerminalOperators.First().OutputSchema.Any(o => o.FieldAlias == "ReleasedBool" && ((o as ValueField)?.FieldType ?? default(Type)) == typeof(bool?))); + Assert.IsTrue(lp.TerminalOperators.First().OutputSchema.Any(o => o.FieldAlias == "ReleasedFloat2" && ((o as ValueField)?.FieldType ?? default(Type)) == typeof(float?))); + } + } + [TestMethod] public void AdvancedTestLogicalPlanner() {