You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Framework/Connected.Expressions/Translation/Optimization/RedundantJoins.cs

87 lines
2.2 KiB

using System.Linq.Expressions;
using Connected.Expressions.Collections;
using Connected.Expressions.Comparers;
using Connected.Expressions.Expressions;
using Connected.Expressions.Visitors;
namespace Connected.Expressions.Translation.Optimization;
internal sealed class RedundantJoins : DatabaseVisitor
{
private RedundantJoins()
{
Map = new Dictionary<Alias, Alias>();
}
private Dictionary<Alias, Alias> Map { get; }
public static Expression Remove(Expression expression)
{
if (new RedundantJoins().Visit(expression) is not Expression redundantJoinExpression)
throw new NullReferenceException(nameof(redundantJoinExpression));
return redundantJoinExpression;
}
protected override Expression VisitJoin(JoinExpression expression)
{
var result = base.VisitJoin(expression);
var joinExpression = result as JoinExpression;
if (joinExpression is not null)
{
var right = joinExpression.Right as AliasedExpression;
if (right is not null)
{
var similarRight = FindSimilarRight(expression.Left as JoinExpression, joinExpression) as AliasedExpression;
if (similarRight is not null)
{
Map.Add(right.Alias, similarRight.Alias);
return joinExpression.Left;
}
}
}
return result;
}
private Expression? FindSimilarRight(JoinExpression? join, JoinExpression compareTo)
{
if (join is null)
return null;
if (join.Join == compareTo.Join)
{
if (join.Right.NodeType == compareTo.Right.NodeType && DatabaseComparer.AreEqual(join.Right, compareTo.Right))
{
if (join.Condition == compareTo.Condition)
return join.Right;
var scope = new ScopedDictionary<Alias, Alias>(null);
scope.Add(((AliasedExpression)join.Right).Alias, ((AliasedExpression)compareTo.Right).Alias);
if (DatabaseComparer.AreEqual(null, scope, join.Condition, compareTo.Condition))
return join.Right;
}
}
var result = FindSimilarRight(join.Left as JoinExpression, compareTo);
result ??= FindSimilarRight(join.Right as JoinExpression, compareTo);
return result;
}
protected override Expression VisitColumn(ColumnExpression column)
{
if (Map.TryGetValue(column.Alias, out Alias? mapped))
return new ColumnExpression(column.Type, column.QueryType, mapped, column.Name);
return column;
}
}