Добавьте файлы проекта.
This commit is contained in:
97
SQLLinter/Infrastructure/Rules/MultiTableAliasRule.cs
Normal file
97
SQLLinter/Infrastructure/Rules/MultiTableAliasRule.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using Microsoft.SqlServer.TransactSql.ScriptDom;
|
||||
using SQLLinter.Common;
|
||||
using SQLLinter.Common.Helpers;
|
||||
using SQLLinter.Infrastructure.Rules.Common;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Rules;
|
||||
|
||||
public class MultiTableAliasRule : BaseRuleVisitor, IRule
|
||||
{
|
||||
private HashSet<string> cteNames = new HashSet<string>();
|
||||
|
||||
|
||||
public override string Text => "Найдена таблица без псевдонимов при объединении нескольких таблиц: {0}";
|
||||
|
||||
public override void Visit(TSqlStatement node)
|
||||
{
|
||||
var childCommonTableExpressionVisitor = new ChildCommonTableExpressionVisitor();
|
||||
node.AcceptChildren(childCommonTableExpressionVisitor);
|
||||
cteNames = childCommonTableExpressionVisitor.CommonTableExpressionIdentifiers;
|
||||
}
|
||||
|
||||
public override void Visit(TableReference node)
|
||||
{
|
||||
void ChildCallback(TSqlFragment childNode)
|
||||
{
|
||||
var dynamicSqlAdjustment = GetDynamicSqlColumnOffset(childNode);
|
||||
var tabsOnLine = ColumnNumberCalculator.CountTabsBeforeToken(childNode.StartLine, childNode.LastTokenIndex, childNode.ScriptTokenStream);
|
||||
var column = ColumnNumberCalculator.GetColumnNumberBeforeToken(tabsOnLine, childNode.ScriptTokenStream[childNode.FirstTokenIndex]);
|
||||
|
||||
string tableName = "";
|
||||
|
||||
if (childNode is NamedTableReference namedTable)
|
||||
{
|
||||
tableName = SQLHelpers.ObjectGetFullName(namedTable.SchemaObject);
|
||||
}
|
||||
|
||||
AddViolation(Name, GetText(tableName), GetLineNumber(childNode), column + dynamicSqlAdjustment);
|
||||
}
|
||||
|
||||
var childTableJoinVisitor = new ChildTableJoinVisitor();
|
||||
node.AcceptChildren(childTableJoinVisitor);
|
||||
|
||||
if (!childTableJoinVisitor.TableJoined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var childTableAliasVisitor = new ChildTableAliasVisitor(ChildCallback, cteNames);
|
||||
node.AcceptChildren(childTableAliasVisitor);
|
||||
}
|
||||
|
||||
public class ChildCommonTableExpressionVisitor : TSqlFragmentVisitor
|
||||
{
|
||||
public HashSet<string> CommonTableExpressionIdentifiers { get; } = new HashSet<string>();
|
||||
|
||||
public override void Visit(CommonTableExpression node)
|
||||
{
|
||||
CommonTableExpressionIdentifiers.Add(node.ExpressionName.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public class ChildTableJoinVisitor : TSqlFragmentVisitor
|
||||
{
|
||||
public bool TableJoined { get; private set; }
|
||||
|
||||
public override void Visit(JoinTableReference node)
|
||||
{
|
||||
TableJoined = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class ChildTableAliasVisitor : TSqlFragmentVisitor
|
||||
{
|
||||
private readonly Action<TSqlFragment> childCallback;
|
||||
|
||||
public ChildTableAliasVisitor(Action<TSqlFragment> errorCallback, HashSet<string> cteNames)
|
||||
{
|
||||
CteNames = cteNames;
|
||||
childCallback = errorCallback;
|
||||
}
|
||||
|
||||
public HashSet<string> CteNames { get; }
|
||||
|
||||
public override void Visit(NamedTableReference node)
|
||||
{
|
||||
if (CteNames.Contains(node.SchemaObject.BaseIdentifier.Value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.Alias == null)
|
||||
{
|
||||
childCallback(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user