using System.Linq.Expressions; using Connected.Expressions.Expressions; using Connected.Expressions.Languages; using Connected.Expressions.Translation.Projections; using Connected.Expressions.Visitors; namespace Connected.Expressions.Evaluation; internal sealed class ExpressionNominator : DatabaseVisitor { private ExpressionNominator(QueryLanguage language, ProjectionAffinity affinity) { Language = language; Affinity = affinity; Candidates = new HashSet(); } private QueryLanguage Language { get; } private ProjectionAffinity Affinity { get; } private HashSet Candidates { get; set; } private bool IsBlocked { get; set; } public static HashSet Nominate(QueryLanguage language, ProjectionAffinity affinity, Expression expression) { var nominator = new ExpressionNominator(language, affinity); nominator.Visit(expression); return nominator.Candidates; } protected override Expression? Visit(Expression? expression) { if (expression is not null) { var saveIsBlocked = IsBlocked; IsBlocked = false; if (Language.MustBeColumn(expression)) Candidates.Add(expression); else { base.Visit(expression); if (!IsBlocked) { if (Language.MustBeColumn(expression) || (Affinity == ProjectionAffinity.Server && Language.CanBeColumn(expression))) Candidates.Add(expression); else IsBlocked = true; } IsBlocked |= saveIsBlocked; } } return expression; } protected override Expression VisitProjection(ProjectionExpression expression) { Visit(expression.Projector); return expression; } }