98 lines
3.2 KiB
C#
98 lines
3.2 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|