From c756b86109a0b5b73e8c0ced1ff6d0288c314c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Pucha=C5=82a?= Date: Thu, 5 Sep 2024 20:15:15 +0200 Subject: [PATCH] fixed query with star throws exception, other fixes around stringify (#68) * fixed query with star throws exception, other fixes around stringify * Update test results badge [skip ci] --------- Co-authored-by: github-actions --- Musoq.Evaluator.Tests/ColumnsTests.cs | 56 +++++++++++++++++++ .../Schema/Basic/BasicEntity.cs | 2 +- .../Schema/Basic/BasicEntityTable.cs | 17 +++--- .../SingleSchemaEvaluatorTests.cs | 28 +++++++--- Musoq.Evaluator.Tests/StringifyTests.cs | 1 + Musoq.Evaluator/Musoq.Evaluator.csproj | 2 +- .../BuildMetadataAndInferTypeVisitor.cs | 6 +- .../Visitors/ToCSharpRewriteTreeVisitor.cs | 14 ++--- Musoq.Parser/Musoq.Parser.csproj | 2 +- Musoq.Parser/Nodes/BooleanNode.cs | 2 +- Musoq.Parser/Nodes/GroupByNode.cs | 14 +++-- Musoq.Parser/Parser.cs | 3 +- badges/tests-badge.svg | 2 +- 13 files changed, 113 insertions(+), 36 deletions(-) diff --git a/Musoq.Evaluator.Tests/ColumnsTests.cs b/Musoq.Evaluator.Tests/ColumnsTests.cs index 6179cd46..8b2e84a2 100644 --- a/Musoq.Evaluator.Tests/ColumnsTests.cs +++ b/Musoq.Evaluator.Tests/ColumnsTests.cs @@ -10,6 +10,62 @@ namespace Musoq.Evaluator.Tests; [TestClass] public class ColumnsTests : BasicEntityTestBase { + [TestMethod] + public void WhenStarUnfoldToMultipleColumns_AndExplicitColumnIsUsedWithinWhere_ShouldPass() + { + const string query = @"select * from #A.entities() a where a.Month = 'january'"; + + var sources = new Dictionary> + { + { + "#A", new[] + { + new BasicEntity("january", 50m), + new BasicEntity("february", 100m), + new BasicEntity("march", 150m) + } + } + }; + + var vm = CreateAndRunVirtualMachine(query, sources); + + var table = vm.Run(); + + Assert.AreEqual(1, table.Count); + Assert.AreEqual(13, table.Columns.Count()); + + Assert.AreEqual("a.Month", table.Columns.ElementAt(6).ColumnName); + Assert.AreEqual("january", table[0].Values[6]); + } + + [TestMethod] + public void WhenStarUnfoldToMultipleColumns_AndExplicitColumnIsUsedWithinSelect_ShouldPass() + { + const string query = @"with p as (select * from #A.entities() a where a.Month = 'january') select * from p"; + + var sources = new Dictionary> + { + { + "#A", new[] + { + new BasicEntity("january", 50m), + new BasicEntity("february", 100m), + new BasicEntity("march", 150m) + } + } + }; + + var vm = CreateAndRunVirtualMachine(query, sources); + + var table = vm.Run(); + + Assert.AreEqual(1, table.Count); + Assert.AreEqual(13, table.Columns.Count()); + + Assert.AreEqual("a.Month", table.Columns.ElementAt(6).ColumnName); + Assert.AreEqual("january", table[0].Values[6]); + } + [TestMethod] public void WhenComplexObjectAccessNonExistingProperty_ShouldFail() { diff --git a/Musoq.Evaluator.Tests/Schema/Basic/BasicEntity.cs b/Musoq.Evaluator.Tests/Schema/Basic/BasicEntity.cs index d925a9ac..ee500bbe 100644 --- a/Musoq.Evaluator.Tests/Schema/Basic/BasicEntity.cs +++ b/Musoq.Evaluator.Tests/Schema/Basic/BasicEntity.cs @@ -111,7 +111,7 @@ public BasicEntity(DateTime time) public int Id { get; set; } - public int[] Array => new[] {0, 1, 2}; + public int[] Array => [0, 1, 2]; public Dictionary Dictionary => new() { diff --git a/Musoq.Evaluator.Tests/Schema/Basic/BasicEntityTable.cs b/Musoq.Evaluator.Tests/Schema/Basic/BasicEntityTable.cs index 5c572a8f..360a0f7b 100644 --- a/Musoq.Evaluator.Tests/Schema/Basic/BasicEntityTable.cs +++ b/Musoq.Evaluator.Tests/Schema/Basic/BasicEntityTable.cs @@ -6,11 +6,8 @@ namespace Musoq.Evaluator.Tests.Schema.Basic { public class BasicEntityTable : ISchemaTable { - public BasicEntityTable() - { - } - - public ISchemaColumn[] Columns { get; } = { + public ISchemaColumn[] Columns { get; } = + [ new SchemaColumn(nameof(BasicEntity.Name), 10, typeof(BasicEntity).GetProperty(nameof(BasicEntity.Name))!.PropertyType), new SchemaColumn(nameof(BasicEntity.City), 11, @@ -30,8 +27,14 @@ public BasicEntityTable() new SchemaColumn(nameof(BasicEntity.Id), 18, typeof(BasicEntity).GetProperty(nameof(BasicEntity.Id))!.PropertyType), new SchemaColumn(nameof(BasicEntity.NullableValue), 19, - typeof(BasicEntity).GetProperty(nameof(BasicEntity.NullableValue))!.PropertyType) - }; + typeof(BasicEntity).GetProperty(nameof(BasicEntity.NullableValue))!.PropertyType), + new SchemaColumn(nameof(BasicEntity.Array), 20, + typeof(BasicEntity).GetProperty(nameof(BasicEntity.Array))!.PropertyType), + new SchemaColumn(nameof(BasicEntity.Other), 21, + typeof(BasicEntity).GetProperty(nameof(BasicEntity.Other))!.PropertyType), + new SchemaColumn(nameof(BasicEntity.Dictionary), 22, + typeof(BasicEntity).GetProperty(nameof(BasicEntity.Dictionary))!.PropertyType), + ]; public ISchemaColumn GetColumnByName(string name) { diff --git a/Musoq.Evaluator.Tests/SingleSchemaEvaluatorTests.cs b/Musoq.Evaluator.Tests/SingleSchemaEvaluatorTests.cs index 61b74055..a8079c3c 100644 --- a/Musoq.Evaluator.Tests/SingleSchemaEvaluatorTests.cs +++ b/Musoq.Evaluator.Tests/SingleSchemaEvaluatorTests.cs @@ -1,7 +1,9 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using Musoq.Evaluator.Exceptions; using Musoq.Evaluator.Tests.Schema.Basic; @@ -586,12 +588,21 @@ public void SimpleShowAllColumnsTest() Assert.AreEqual("NullableValue", table.Columns.ElementAt(10).ColumnName); Assert.AreEqual(typeof(int?), table.Columns.ElementAt(10).ColumnType); + + Assert.AreEqual("Array", table.Columns.ElementAt(11).ColumnName); + Assert.AreEqual(typeof(int[]), table.Columns.ElementAt(11).ColumnType); + + Assert.AreEqual("Other", table.Columns.ElementAt(12).ColumnName); + Assert.AreEqual(typeof(BasicEntity), table.Columns.ElementAt(12).ColumnType); + + Assert.AreEqual("Dictionary", table.Columns.ElementAt(13).ColumnName); + Assert.AreEqual(typeof(Dictionary), table.Columns.ElementAt(13).ColumnType); - Assert.AreEqual("Name2", table.Columns.ElementAt(11).ColumnName); - Assert.AreEqual(typeof(string), table.Columns.ElementAt(11).ColumnType); + Assert.AreEqual("Name2", table.Columns.ElementAt(14).ColumnName); + Assert.AreEqual(typeof(string), table.Columns.ElementAt(14).ColumnType); - Assert.AreEqual("SelfString", table.Columns.ElementAt(12).ColumnName); - Assert.AreEqual(typeof(string), table.Columns.ElementAt(12).ColumnType); + Assert.AreEqual("SelfString", table.Columns.ElementAt(15).ColumnName); + Assert.AreEqual(typeof(string), table.Columns.ElementAt(15).ColumnType); Assert.AreEqual(1, table.Count); @@ -606,8 +617,11 @@ public void SimpleShowAllColumnsTest() Assert.AreEqual(DateTime.MaxValue, table[0].Values[8]); Assert.AreEqual(5, table[0].Values[9]); Assert.AreEqual(null, table[0].Values[10]); - Assert.AreEqual("ABBA", table[0].Values[11]); - Assert.AreEqual("TEST STRING", table[0].Values[12]); + Assert.IsNotNull(table[0].Values[11]); + Assert.AreEqual(entity, table[0].Values[12]); + Assert.IsNotNull(table[0].Values[13]); + Assert.AreEqual("ABBA", table[0].Values[14]); + Assert.AreEqual("TEST STRING", table[0].Values[15]); } [TestMethod] diff --git a/Musoq.Evaluator.Tests/StringifyTests.cs b/Musoq.Evaluator.Tests/StringifyTests.cs index ccba70f6..e451e2e6 100644 --- a/Musoq.Evaluator.Tests/StringifyTests.cs +++ b/Musoq.Evaluator.Tests/StringifyTests.cs @@ -55,6 +55,7 @@ public class StringifyTests : BasicEntityTestBase [DataRow("select s.Column1, s.Column2 from #some.thing() s where s.Column2 like 'A%' order by s.Column1 skip 4 take 8")] [DataRow("select s.Column1, s.Column2 from #some.thing() s group by s.Column1, s.Column2 order by s.Column1 skip 5 take 10")] [DataRow("select s.Column1, s.Column2 from #some.thing() s where s.Column1 > 0 group by s.Column2 order by s.Column1 skip 2 take 5")] + [DataRow("select t.Name, Count(t.Name) from #some.thing(true) t group by t.Name having Count(t.Name) > 1")] public void WhenToStringCalled_ShouldReturnSameQuery(string query) { diff --git a/Musoq.Evaluator/Musoq.Evaluator.csproj b/Musoq.Evaluator/Musoq.Evaluator.csproj index e3cb6db6..92c512c9 100644 --- a/Musoq.Evaluator/Musoq.Evaluator.csproj +++ b/Musoq.Evaluator/Musoq.Evaluator.csproj @@ -3,7 +3,7 @@ net8.0 true - 6.0.4 + 6.0.5 Jakub Puchała Musoq https://github.com/Puchaczov/Musoq diff --git a/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypeVisitor.cs b/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypeVisitor.cs index 15929e4e..f64fd750 100644 --- a/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypeVisitor.cs +++ b/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypeVisitor.cs @@ -435,7 +435,7 @@ public void Visit(AllColumnsNode node) TextSpan.Empty ), i, - tableSymbol.HasAlias ? _identifier : column.ColumnName); + tableSymbol.HasAlias ? $"{_identifier}.{column.ColumnName}" : column.ColumnName); } Nodes.Push(node); @@ -752,8 +752,8 @@ public void Visit(SchemaFromNode node) _queryAlias = AliasGenerator.CreateAliasIfEmpty(node.Alias, _generatedAliases, _schemaFromKey.ToString()); _generatedAliases.Add(_queryAlias); - var table = _currentScope.Name != "Desc" - ? schema.GetTableByName( + var isDesc = _currentScope.Name == "Desc"; + var table = !isDesc ? schema.GetTableByName( node.Method, new RuntimeContext( CancellationToken.None, diff --git a/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs b/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs index 06c421f3..58088d36 100644 --- a/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs +++ b/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs @@ -45,12 +45,12 @@ public class ToCSharpRewriteTreeVisitor : IToCSharpTranslationExpressionVisitor private const char EscapeQuoteStringCharacterReplacement = '\''; private readonly Dictionary _inMemoryTableIndexes = new(); - private readonly List _loadedAssemblies = new(); + private readonly List _loadedAssemblies = []; - private readonly List _members = new(); + private readonly List _members = []; private readonly Stack _methodNames = new(); - private readonly List _namespaces = new(); + private readonly List _namespaces = []; private readonly IDictionary _setOperatorFieldIndexes; private readonly Dictionary _typesToInstantiate = new(); @@ -948,11 +948,11 @@ public void Visit(ArgsListNode node) for (var i = 0; i < node.Args.Length; i++) args = args.Add(SyntaxFactory.Argument((ExpressionSyntax) Nodes.Pop())); - var rargs = SyntaxFactory.SeparatedList(); + var rArgs = SyntaxFactory.SeparatedList(); - for (var i = args.Count - 1; i >= 0; i--) rargs = rargs.Add(args[i]); + for (var i = args.Count - 1; i >= 0; i--) rArgs = rArgs.Add(args[i]); - Nodes.Push(SyntaxFactory.ArgumentList(rargs)); + Nodes.Push(SyntaxFactory.ArgumentList(rArgs)); } @@ -1350,7 +1350,7 @@ public void Visit(JoinInMemoryWithSourceTableFromNode node) } foreach (var column in fullTransitionTable.GetColumns( - fullTransitionTable.CompoundTables[fullTransitionTable.CompoundTables.Length - 1])) + fullTransitionTable.CompoundTables[^1])) { expressions.Add( SyntaxFactory.ElementAccessExpression( diff --git a/Musoq.Parser/Musoq.Parser.csproj b/Musoq.Parser/Musoq.Parser.csproj index fe861415..8940be42 100644 --- a/Musoq.Parser/Musoq.Parser.csproj +++ b/Musoq.Parser/Musoq.Parser.csproj @@ -3,7 +3,7 @@ net8.0 true - 3.0.10 + 3.0.11 Jakub Puchała Musoq https://github.com/Puchaczov/Musoq diff --git a/Musoq.Parser/Nodes/BooleanNode.cs b/Musoq.Parser/Nodes/BooleanNode.cs index af7f530f..3cf89643 100644 --- a/Musoq.Parser/Nodes/BooleanNode.cs +++ b/Musoq.Parser/Nodes/BooleanNode.cs @@ -21,7 +21,7 @@ public BooleanNode(bool value) public override string ToString() { - return Value.ToString(CultureInfo.InvariantCulture); + return Value ? "true" : "false"; } public override void Accept(IExpressionVisitor visitor) diff --git a/Musoq.Parser/Nodes/GroupByNode.cs b/Musoq.Parser/Nodes/GroupByNode.cs index 104ea253..c04e5a9d 100644 --- a/Musoq.Parser/Nodes/GroupByNode.cs +++ b/Musoq.Parser/Nodes/GroupByNode.cs @@ -6,7 +6,7 @@ namespace Musoq.Parser.Nodes public class GroupByNode : Node { public GroupByNode(FieldNode[] fields, HavingNode node) - { + { Fields = fields; Having = node; var fieldsIds = fields.Length == 0 ? string.Empty : fields.Select(f => f.Id).Aggregate((a, b) => a + b); @@ -28,11 +28,13 @@ public override void Accept(IExpressionVisitor visitor) public override string ToString() { - var fields = Fields.Length == 0 - ? string.Empty - : Fields.Select(f => f.ToString()).Aggregate((a, b) => $"{a.ToString()}, {b.ToString()}"); - return - $"group by {fields}{Having?.ToString()}"; + var fields = Fields.Length == 0 ? string.Empty : Fields.Select(f => f.ToString()).Aggregate((a, b) => $"{a.ToString()}, {b.ToString()}"); + var groupBy = $"group by {fields}"; + + if (Having == null) + return groupBy; + + return $"{groupBy} {Having.ToString()}"; } } } \ No newline at end of file diff --git a/Musoq.Parser/Parser.cs b/Musoq.Parser/Parser.cs index f0203881..c19410e5 100644 --- a/Musoq.Parser/Parser.cs +++ b/Musoq.Parser/Parser.cs @@ -369,7 +369,8 @@ private GroupByNode ComposeGroupByNode() Consume(TokenType.GroupBy); var fields = ComposeFields(); - + + if (fields.Length == 0) throw new NotSupportedException("Group by clause does not have any fields."); if (Current.TokenType != TokenType.Having) return new GroupByNode(fields, null); Consume(TokenType.Having); diff --git a/badges/tests-badge.svg b/badges/tests-badge.svg index 143a308c..c041d201 100644 --- a/badges/tests-badge.svg +++ b/badges/tests-badge.svg @@ -1 +1 @@ -tests: 979/983 (0 failed, 4 skipped)tests979/983 (0 failed, 4 skipped) \ No newline at end of file +tests: 1030/1034 (0 failed, 4 skipped)tests1030/1034 (0 failed, 4 skipped) \ No newline at end of file