diff --git a/Musoq.Evaluator.Tests/CrossApplyCteTests.cs b/Musoq.Evaluator.Tests/CrossApplyCteTests.cs index c8de47c..b313731 100644 --- a/Musoq.Evaluator.Tests/CrossApplyCteTests.cs +++ b/Musoq.Evaluator.Tests/CrossApplyCteTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Musoq.Evaluator.Tests.Schema.Generic; @@ -35,6 +36,11 @@ private class CrossApplyClass3 public string[] Skills { get; set; } } + public class CrossApplyClass4 + { + public string Name { get; set; } + } + [TestMethod] public void WhenSchemaMethodCrossAppliedWithAnotherSchema_WithinCte_ShouldPass() { @@ -320,4 +326,45 @@ with first as ( Assert.AreEqual("Name3", table[8].Values[0]); Assert.AreEqual("Skill9", table[8].Values[1]); } + + [TestMethod] + public void WhenCrossApplyComponentsMustInjectMultipleEntities_ShouldNotThrow() + { + var query = """ + with first as ( + select + r.AggregateValues(r.Name) as Name1, + r.AggregateValues(r.Name) as Name2 + from #schema.first() r + cross apply r.JustReturnArrayOfString() b + cross apply r.JustReturnArrayOfString() c + group by 'fake' + ) + select + b.Name1, + b.Name2, + p.Value + from first b + inner join #schema.first() r on 1 = 1 + cross apply r.MethodArrayOfStrings(r.TestMethodWithInjectEntityAndParameter(b.Name1), r.TestMethodWithInjectEntityAndParameter(b.Name2)) p + """; + + var firstSource = new List<CrossApplyClass4> + { + new() {Name = "Name1"} + }.ToArray(); + + var vm = CreateAndRunVirtualMachine( + query, + firstSource); + + try + { + vm.Run(); + } + catch (Exception) + { + Assert.Fail($"Expected not to throw exception but got: "); + } + } } \ No newline at end of file diff --git a/Musoq.Evaluator.Tests/MethodInvocationTests.cs b/Musoq.Evaluator.Tests/MethodInvocationTests.cs index 1cf872f..82341b3 100644 --- a/Musoq.Evaluator.Tests/MethodInvocationTests.cs +++ b/Musoq.Evaluator.Tests/MethodInvocationTests.cs @@ -235,8 +235,8 @@ cross apply x.JustReturnArrayOfString() b select p.Value from first b - inner join #A.entities() r2 on 1 = 1 - cross apply r2.MethodArrayOfStrings(r2.TestMethodWithInjectEntityAndParameter(b.Name), r2.TestMethodWithInjectEntityAndParameter(b.Name)) p + inner join #A.entities() r on 1 = 1 + cross apply r.MethodArrayOfStrings(r.TestMethodWithInjectEntityAndParameter(b.Name), r.TestMethodWithInjectEntityAndParameter(b.Name)) p """; var sources = new Dictionary<string, IEnumerable<BasicEntity>>() diff --git a/Musoq.Evaluator.Tests/Schema/Generic/GenericLibrary.cs b/Musoq.Evaluator.Tests/Schema/Generic/GenericLibrary.cs index aefe7ed..4c18b18 100644 --- a/Musoq.Evaluator.Tests/Schema/Generic/GenericLibrary.cs +++ b/Musoq.Evaluator.Tests/Schema/Generic/GenericLibrary.cs @@ -1,5 +1,28 @@ using Musoq.Plugins; +using Musoq.Plugins.Attributes; namespace Musoq.Evaluator.Tests.Schema.Generic; -public class GenericLibrary : LibraryBase; \ No newline at end of file +public class GenericLibrary : LibraryBase +{ + [BindableMethod] + public string[] JustReturnArrayOfString() + { + return ["1", "2", "3"]; + } + + [BindableMethod] + public string TestMethodWithInjectEntityAndParameter(string name) + { + return name; + } + + [BindableMethod] + public string[] MethodArrayOfStrings<TEntity>([InjectSpecificSource(typeof(object))] TEntity entity, string name1, string name2) + { + return [ + name1, + name2 + ]; + } +} \ No newline at end of file diff --git a/Musoq.Evaluator/Musoq.Evaluator.csproj b/Musoq.Evaluator/Musoq.Evaluator.csproj index 533e465..e34e939 100644 --- a/Musoq.Evaluator/Musoq.Evaluator.csproj +++ b/Musoq.Evaluator/Musoq.Evaluator.csproj @@ -3,7 +3,7 @@ <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <GeneratePackageOnBuild>true</GeneratePackageOnBuild> - <Version>7.5.1</Version> + <Version>7.5.2</Version> <Authors>Jakub Puchała</Authors> <Product>Musoq</Product> <PackageProjectUrl>https://github.com/Puchaczov/Musoq</PackageProjectUrl> diff --git a/Musoq.Evaluator/Tables/TableRowSource.cs b/Musoq.Evaluator/Tables/TableRowSource.cs index 1ba8f24..012a46f 100644 --- a/Musoq.Evaluator/Tables/TableRowSource.cs +++ b/Musoq.Evaluator/Tables/TableRowSource.cs @@ -6,14 +6,16 @@ namespace Musoq.Evaluator.Tables; public class TableRowSource : RowSource { + private static readonly object[] DiscardedContexts = [new DiscardedRowContext()]; + private readonly IDictionary<string, int> _columnToIndexMap; private readonly Table _table; - private readonly bool _skipContext; + private readonly bool _discardedContext; - public TableRowSource(Table rowSource, bool skipContext) + public TableRowSource(Table rowSource, bool discardContext) { _table = rowSource; - _skipContext = skipContext; + _discardedContext = discardContext; _columnToIndexMap = new Dictionary<string, int>(); @@ -21,11 +23,13 @@ public TableRowSource(Table rowSource, bool skipContext) _columnToIndexMap.Add(column.ColumnName, column.ColumnIndex); } - public override IEnumerable<IObjectResolver> Rows => _skipContext ? RowsWithSkippedContexts : RowsWithContexts; + public override IEnumerable<IObjectResolver> Rows => _discardedContext ? RowsWithDiscardedContexts : RowsWithContexts; private IEnumerable<IObjectResolver> RowsWithContexts => _table.Select(row => new RowResolver((ObjectsRow)row, _columnToIndexMap)); - private IEnumerable<IObjectResolver> RowsWithSkippedContexts => - _table.Select(row => new RowResolver(new ObjectsRow(row.Values, row.Values), _columnToIndexMap)); + private IEnumerable<IObjectResolver> RowsWithDiscardedContexts => + _table.Select(row => new RowResolver(new ObjectsRow(row.Values, DiscardedContexts), _columnToIndexMap)); + + private class DiscardedRowContext; } \ No newline at end of file diff --git a/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypesVisitor.cs b/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypesVisitor.cs index 15fb014..57fcf2b 100644 --- a/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypesVisitor.cs +++ b/Musoq.Evaluator/Visitors/BuildMetadataAndInferTypesVisitor.cs @@ -1482,8 +1482,11 @@ private void VisitAccessMethod(AccessMethodNode node, Func<FunctionToken, Node, method = reducedMethod; } - if (!isAggregateMethod && method.IsGenericMethod && !method.IsConstructedGenericMethod && - TryConstructGenericMethod(method, args, out var constructedMethod)) + if ( + !isAggregateMethod && + method.IsGenericMethod && + !method.IsConstructedGenericMethod && + TryConstructGenericMethod(method, args, entityType, out var constructedMethod)) { method = constructedMethod; } @@ -2076,7 +2079,7 @@ private static bool TryReduceDimensions(MethodInfo method, ArgsListNode args, ou return true; } - private static bool TryConstructGenericMethod(MethodInfo methodInfo, ArgsListNode args, out MethodInfo constructedMethod) + private static bool TryConstructGenericMethod(MethodInfo methodInfo, ArgsListNode args, Type entity, out MethodInfo constructedMethod) { var genericArguments = methodInfo.GetGenericArguments(); var genericArgumentsDistinct = new List<Type>(); @@ -2090,6 +2093,10 @@ private static bool TryConstructGenericMethod(MethodInfo methodInfo, ArgsListNod { i = 1; shiftArgsWhenInjectSpecificSourcePresent = 1; + if ((genericArgument.IsGenericParameter || genericArgument.IsGenericMethodParameter) && parameters[0].ParameterType.IsGenericParameter) + { + genericArgumentsDistinct.Add(entity); + } } for (; i < parameters.Length; i++) { @@ -2138,7 +2145,7 @@ private static bool TryConstructGenericMethod(MethodInfo methodInfo, ArgsListNod } var genericArgumentsConcreteTypes = genericArgumentsDistinct.Distinct().ToArray(); - + constructedMethod = methodInfo.MakeGenericMethod(genericArgumentsConcreteTypes); return true; } diff --git a/Musoq.Evaluator/Visitors/RewriteQueryVisitor.cs b/Musoq.Evaluator/Visitors/RewriteQueryVisitor.cs index e1408ae..c468149 100644 --- a/Musoq.Evaluator/Visitors/RewriteQueryVisitor.cs +++ b/Musoq.Evaluator/Visitors/RewriteQueryVisitor.cs @@ -463,8 +463,6 @@ public void Visit(QueryNode node) var scoreWhere = where; var scoreOrderBy = orderBy; - QueryNode query; - var splitNodes = new List<Node>(); var source = from.Alias.ToRowsSource().WithRowsUsage(); @@ -860,7 +858,7 @@ public void Visit(QueryNode node) modifiedOrderBy = new OrderByNode(splitOrderBy); } - query = new DetailedQueryNode( + QueryNode query = new DetailedQueryNode( outSelect, new Parser.ExpressionFromNode( new InMemoryGroupedFromNode(returnScore)), @@ -887,8 +885,6 @@ public void Visit(QueryNode node) if (IsQueryWithMixedAggregateAndNonAggregateMethods(split)) { - query = new InternalQueryNode(select, from, where, null, orderBy, skip, take, - CreateRefreshMethods(usedRefreshMethods)); throw new NotImplementedException("Mixing aggregate and non aggregate methods is not implemented yet"); } diff --git a/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs b/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs index 50c15a4..69348f0 100644 --- a/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs +++ b/Musoq.Evaluator/Visitors/ToCSharpRewriteTreeVisitor.cs @@ -680,6 +680,7 @@ public void Visit(AccessMethodNode node) (IndexBasedContextsPositionsSymbol) _scope.ScopeSymbolTable.GetSymbol(MetaAttributes .PreformatedContexts); var orderNumber = int.Parse(_scope[MetaAttributes.OrderNumber]); + currentContext = preformattedContexts.GetIndexFor(orderNumber, node.Alias); } else @@ -2067,7 +2068,7 @@ public void Visit(InMemoryTableFromNode node) _inMemoryTableIndexes[ node.VariableName]))))))); - var literalFalseArgument = SyntaxFactory.Argument( + var literalTrueArgument = SyntaxFactory.Argument( SyntaxFactory.LiteralExpression( SyntaxKind.TrueLiteralExpression)); @@ -2084,7 +2085,7 @@ public void Visit(InMemoryTableFromNode node) SyntaxFactory.ArgumentList( SyntaxFactory.SeparatedList([ tableArgument, - literalFalseArgument + literalTrueArgument ]))))))))); } diff --git a/badges/tests-badge.svg b/badges/tests-badge.svg index 259e19c..1efa4ae 100644 --- a/badges/tests-badge.svg +++ b/badges/tests-badge.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="224" height="20" role="img" aria-label="tests: 1306/1310 (0 failed, 4 skipped)"><title>tests: 1306/1310 (0 failed, 4 skipped)</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="224" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="187" height="20" fill="#dfb317"/><rect width="224" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="1295" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="1770">1306/1310 (0 failed, 4 skipped)</text><text x="1295" y="140" transform="scale(.1)" fill="#fff" textLength="1770">1306/1310 (0 failed, 4 skipped)</text></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="224" height="20" role="img" aria-label="tests: 1307/1311 (0 failed, 4 skipped)"><title>tests: 1307/1311 (0 failed, 4 skipped)</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="224" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="187" height="20" fill="#dfb317"/><rect width="224" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="1295" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="1770">1307/1311 (0 failed, 4 skipped)</text><text x="1295" y="140" transform="scale(.1)" fill="#fff" textLength="1770">1307/1311 (0 failed, 4 skipped)</text></g></svg> \ No newline at end of file