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

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,209 @@
using Microsoft.SqlServer.TransactSql.ScriptDom;
using SQLLinter.Common;
using System.Collections.Generic;
namespace SQLLinter.Infrastructure.Rules;
public class DuplicateAliasRule : BaseRuleVisitor
{
public override string Text => "Алиасы таблиц должны быть уникальными: {0}";
private readonly Dictionary<int, HashSet<string>> _activeAliases = new();
private int _dmlDepth = 0;
private bool _insideApply = false;
private void EnterDmlScope(bool inherit)
{
_dmlDepth++;
if (_dmlDepth == 1 || !inherit)
{
_activeAliases[_dmlDepth] = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
}
else
{
// дочерний скоуп видит алиасы родителя (для глобальной уникальности в рамках одного DML)
var parent = _activeAliases[_dmlDepth - 1];
_activeAliases[_dmlDepth] = new HashSet<string>(parent, StringComparer.OrdinalIgnoreCase);
}
}
private void ExitDmlScope()
{
if (_activeAliases.ContainsKey(_dmlDepth))
_activeAliases.Remove(_dmlDepth);
_dmlDepth--;
}
private void RegisterAlias(string alias, TSqlFragment node)
{
if (_dmlDepth == 0) return; // вне DML не проверяем
var set = _activeAliases[_dmlDepth];
if (set.Contains(alias))
{
AddViolation(node, "[" + alias + "]");
}
else
{
set.Add(alias);
}
}
// Корневые DML-выражения
public override void ExplicitVisit(SelectStatement node)
{
EnterDmlScope(true);
base.ExplicitVisit(node);
ExitDmlScope();
}
public override void ExplicitVisit(InsertStatement node)
{
EnterDmlScope(true);
base.ExplicitVisit(node);
ExitDmlScope();
}
public override void ExplicitVisit(UpdateStatement node)
{
EnterDmlScope(true);
base.ExplicitVisit(node);
ExitDmlScope();
}
public override void ExplicitVisit(DeleteStatement node)
{
EnterDmlScope(true);
base.ExplicitVisit(node);
ExitDmlScope();
}
public override void ExplicitVisit(MergeStatement node)
{
EnterDmlScope(true);
base.ExplicitVisit(node);
ExitDmlScope();
}
// IF: каждая ветка - отдельный скоуп
public override void ExplicitVisit(IfStatement node)
{
EnterDmlScope(false);
node.ThenStatement.Accept(this);
ExitDmlScope();
if (node.ElseStatement != null)
{
EnterDmlScope(false);
node.ElseStatement.Accept(this);
ExitDmlScope();
}
base.ExplicitVisit(node);
}
// UNION: каждая часть - отдельный скоуп
public override void ExplicitVisit(BinaryQueryExpression node)
{
EnterDmlScope(true);
node.FirstQueryExpression.Accept(this);
ExitDmlScope();
EnterDmlScope(true);
node.SecondQueryExpression.Accept(this);
ExitDmlScope();
//base.ExplicitVisit(node);
}
// CTE: регистрируем имя, тело - в дочернем скоупе
public override void ExplicitVisit(CommonTableExpression node)
{
if (node.ExpressionName?.Value is { Length: > 0 } cteAlias)
RegisterAlias(cteAlias, node);
EnterDmlScope(false);
node.QueryExpression.Accept(this);
ExitDmlScope();
base.ExplicitVisit(node);
}
// Производная таблица: регистрируем её алиас; внутренняя QueryExpression обойдётся базой
public override void ExplicitVisit(QueryDerivedTable node)
{
if (node.Alias?.Value is { Length: > 0 } a)
RegisterAlias(a, node);
// тело подзапроса в FROM - свой локальный скоуп
EnterDmlScope(_insideApply);
node.QueryExpression.Accept(this);
ExitDmlScope();
}
// CROSS APPLY
public override void ExplicitVisit(UnqualifiedJoin node)
{
if (node.UnqualifiedJoinType == UnqualifiedJoinType.CrossApply ||
node.UnqualifiedJoinType == UnqualifiedJoinType.OuterApply)
{
var prev = _insideApply;
_insideApply = true;
// обходим без нового скоупа, т.к. алиасы учитываются глобально
node.FirstTableReference.Accept(this);
node.SecondTableReference.Accept(this);
_insideApply = prev;
}
else
{
base.ExplicitVisit(node);
}
}
// Табличные источники с алиасами
public override void ExplicitVisit(NamedTableReference node)
{
if (node.Alias?.Value is { Length: > 0 } a)
RegisterAlias(a, node);
base.ExplicitVisit(node);
}
public override void ExplicitVisit(VariableTableReference node)
{
if (node.Alias?.Value is { Length: > 0 } a)
RegisterAlias(a, node);
base.ExplicitVisit(node);
}
public override void ExplicitVisit(SchemaObjectFunctionTableReference node)
{
if (node.Alias?.Value is { Length: > 0 } a)
RegisterAlias(a, node);
base.ExplicitVisit(node);
}
public override void ExplicitVisit(PivotedTableReference node)
{
if (node.Alias?.Value is { Length: > 0 } a)
RegisterAlias(a, node);
base.ExplicitVisit(node);
}
public override void ExplicitVisit(UnpivotedTableReference node)
{
if (node.Alias?.Value is { Length: > 0 } a)
RegisterAlias(a, node);
base.ExplicitVisit(node);
}
// Сброс состояния перед анализом скрипта
public override void ExplicitVisit(TSqlScript node)
{
_dmlDepth = 0;
_activeAliases.Clear();
base.ExplicitVisit(node);
}
}