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

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,118 @@
using Microsoft.SqlServer.TransactSql.ScriptDom;
using SQLLinter.Common;
namespace SQLLinter.Infrastructure.Rules;
public class CrossDatabaseTransactionRule : BaseRuleVisitor, IRule
{
public override string Text => "Межбазовые вставки или обновления, включенные в транзакцию, могут привести к повреждению данных.";
public override void Visit(TSqlBatch node)
{
var childTransactionVisitor = new ChildTransactionVisitor();
node.Accept(childTransactionVisitor);
foreach (var transaction in childTransactionVisitor.TransactionLists)
{
var childInsertUpdateQueryVisitor = new ChildInsertUpdateQueryVisitor(transaction);
node.Accept(childInsertUpdateQueryVisitor);
if (childInsertUpdateQueryVisitor.DatabasesUpdated.Count > 1)
{
AddViolation(
Name,
Text,
GetLineNumber(transaction.Begin),
GetColumnNumber(transaction.Begin));
}
}
}
public class TrackedTransaction
{
public BeginTransactionStatement Begin { get; set; }
public CommitTransactionStatement Commit { get; set; }
}
public class ChildTransactionVisitor : TSqlFragmentVisitor
{
public List<TrackedTransaction> TransactionLists { get; } = new List<TrackedTransaction>();
public override void Visit(BeginTransactionStatement node)
{
TransactionLists.Add(new TrackedTransaction { Begin = node });
}
public override void Visit(CommitTransactionStatement node)
{
var firstUncomitted = TransactionLists.LastOrDefault(x => x.Commit == null);
if (firstUncomitted != null)
{
firstUncomitted.Commit = node;
}
}
}
public class ChildInsertUpdateQueryVisitor : TSqlFragmentVisitor
{
private readonly TrackedTransaction transaction;
private readonly ChildDatabaseNameVisitor childDatabaseNameVisitor = new ChildDatabaseNameVisitor();
public ChildInsertUpdateQueryVisitor(TrackedTransaction transaction)
{
this.transaction = transaction;
}
public HashSet<string> DatabasesUpdated { get; } = new HashSet<string>();
public override void Visit(InsertStatement node)
{
GetDatabasesUpdated(node);
}
public override void Visit(UpdateStatement node)
{
GetDatabasesUpdated(node);
}
private void GetDatabasesUpdated(TSqlFragment node)
{
if (IsWithinTransaction(node))
{
node.Accept(childDatabaseNameVisitor);
DatabasesUpdated.UnionWith(childDatabaseNameVisitor.DatabasesUpdated);
}
}
private bool IsWithinTransaction(TSqlFragment node)
{
if (node.StartLine == transaction.Begin?.StartLine &&
node.StartColumn < transaction.Begin?.StartColumn)
{
return false;
}
if (node.StartLine == transaction.Commit?.StartLine &&
node.StartColumn > transaction.Commit?.StartColumn)
{
return false;
}
return node.StartLine >= transaction.Begin?.StartLine && node.StartLine <= transaction.Commit?.StartLine;
}
}
public class ChildDatabaseNameVisitor : TSqlFragmentVisitor
{
public HashSet<string> DatabasesUpdated { get; } = new HashSet<string>();
public override void Visit(NamedTableReference node)
{
if (node.SchemaObject.DatabaseIdentifier != null)
{
DatabasesUpdated.Add(node.SchemaObject.DatabaseIdentifier.Value);
}
}
}
}