Добавьте файлы проекта.

This commit is contained in:
2025-12-07 08:52:05 +03:00
parent 95344cd7a7
commit 226b6b6b21
118 changed files with 5249 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
using Microsoft.SqlServer.TransactSql.ScriptDom;
using SQLLinter.Common;
using SQLLinter.Infrastructure.Rules.Common;
namespace SQLLinter.Infrastructure.Rules;
public class NonSargableRule : BaseRuleVisitor, IRule
{
private readonly List<TSqlFragment> errorsReported = new();
public override string Text => "Выполнение функций с предложениями фильтра или предикатами соединения может вызвать проблемы с производительностью.";
public override void Visit(JoinTableReference node)
{
var predicateExpressionVisitor = new PredicateVisitor();
node.AcceptChildren(predicateExpressionVisitor);
var multiClauseQuery = predicateExpressionVisitor.PredicatesFound;
var joinVisitor = new JoinQueryVisitor(VisitorCallback, multiClauseQuery);
node.AcceptChildren(joinVisitor);
}
public override void Visit(WhereClause node)
{
var predicateExpressionVisitor = new PredicateVisitor();
node.Accept(predicateExpressionVisitor);
var multiClauseQuery = predicateExpressionVisitor.PredicatesFound;
var childVisitor = new FunctionVisitor(VisitorCallback, multiClauseQuery);
node.Accept(childVisitor);
}
private void VisitorCallback(TSqlFragment childNode)
{
if (errorsReported.Contains(childNode))
{
return;
}
var dynamicSqlColumnAdjustment = GetDynamicSqlColumnOffset(childNode);
errorsReported.Add(childNode);
AddViolation(Name, Text, GetLineNumber(childNode), ColumnNumberCalculator.GetNodeColumnPosition(childNode) + dynamicSqlColumnAdjustment);
}
private class JoinQueryVisitor : TSqlFragmentVisitor
{
private readonly Action<TSqlFragment> childCallback;
private readonly bool isMultiClauseQuery;
public JoinQueryVisitor(Action<TSqlFragment> childCallback, bool multiClauseQuery)
{
this.childCallback = childCallback;
isMultiClauseQuery = multiClauseQuery;
}
public override void Visit(BooleanComparisonExpression node)
{
var childVisitor = new FunctionVisitor(childCallback, isMultiClauseQuery);
node.Accept(childVisitor);
}
}
private class PredicateVisitor : TSqlFragmentVisitor
{
public bool PredicatesFound { get; private set; }
public override void Visit(BooleanBinaryExpression node)
{
PredicatesFound = true;
}
}
private class FunctionVisitor : TSqlFragmentVisitor
{
private readonly bool isMultiClause;
private readonly Action<TSqlFragment> childCallback;
private bool hasColumnReferenceParameter;
public FunctionVisitor(Action<TSqlFragment> errorCallback, bool isMultiClause)
{
childCallback = errorCallback;
this.isMultiClause = isMultiClause;
}
public override void Visit(FunctionCall node)
{
switch (node.FunctionName.Value.ToUpper())
{
// разрешить предикаты isnull при наличии других фильтров
case "ISNULL" when isMultiClause:
return;
case "DATEADD":
case "DATEDIFF":
case "DATEDIFF_BIG":
case "DATENAME":
case "DATEPART":
case "DATETRUNC":
case "DATE_BUCKET":
hasColumnReferenceParameter = true;
break;
}
FindColumnReferences(node);
}
public override void Visit(LeftFunctionCall node)
{
FindColumnReferences(node);
}
public override void Visit(RightFunctionCall node)
{
FindColumnReferences(node);
}
public override void Visit(ConvertCall node)
{
FindColumnReferences(node);
}
public override void Visit(CastCall node)
{
FindColumnReferences(node);
}
private void FindColumnReferences(TSqlFragment node)
{
var columnReferenceVisitor = new ColumnReferenceVisitor();
node.AcceptChildren(columnReferenceVisitor);
if (columnReferenceVisitor.ColumnReferenceFound && (!hasColumnReferenceParameter || columnReferenceVisitor.ColumnReferenceCount > 1))
{
childCallback(node);
}
}
}
private class ColumnReferenceVisitor : TSqlFragmentVisitor
{
public bool ColumnReferenceFound { get; private set; }
public int ColumnReferenceCount { get; private set; }
public override void Visit(ColumnReferenceExpression node)
{
ColumnReferenceCount++;
ColumnReferenceFound = true;
}
}
}