using System.Linq.Expressions; using Connected.Expressions.Expressions; using Connected.Expressions.Visitors; namespace Connected.Expressions.Translation; internal sealed class Subqueries : DatabaseVisitor { private Subqueries(IEnumerable selectsToRemove) { SelectsToRemove = new HashSet(selectsToRemove); Map = SelectsToRemove.ToDictionary(d => d.Alias, d => d.Columns.ToDictionary(d2 => d2.Name, d2 => d2.Expression)); } private HashSet SelectsToRemove { get; set; } private Dictionary> Map { get; set; } public static SelectExpression Remove(SelectExpression outerSelect, params SelectExpression[] selectsToRemove) { return Remove(outerSelect, (IEnumerable)selectsToRemove); } public static SelectExpression Remove(SelectExpression outerSelect, IEnumerable selectsToRemove) { if (new Subqueries(selectsToRemove).Visit(outerSelect) is not SelectExpression selectRemoveExpression) throw new NullReferenceException(nameof(selectRemoveExpression)); return selectRemoveExpression; } public static ProjectionExpression Remove(ProjectionExpression projection, params SelectExpression[] selectsToRemove) { return Remove(projection, (IEnumerable)selectsToRemove); } public static ProjectionExpression Remove(ProjectionExpression projection, IEnumerable selectsToRemove) { if (new Subqueries(selectsToRemove).Visit(projection) is not ProjectionExpression projectionRemoveExpression) throw new NullReferenceException(nameof(projectionRemoveExpression)); return projectionRemoveExpression; } protected override Expression VisitSelect(SelectExpression expression) { if (SelectsToRemove.Contains(expression)) { if (Visit(expression.From) is not Expression fromExpression) throw new NullReferenceException(nameof(fromExpression)); return fromExpression; } else return base.VisitSelect(expression); } protected override Expression VisitColumn(ColumnExpression expression) { if (Map.TryGetValue(expression.Alias, out Dictionary? nameMap)) { if (nameMap.TryGetValue(expression.Name, out Expression? expr)) { if (Visit(expr) is not Expression columnExpression) throw new NullReferenceException(nameof(columnExpression)); return columnExpression; } throw new NullReferenceException($"Reference to undefined column ({expression.Name})"); } return expression; } }