using Microsoft.SqlServer.TransactSql.ScriptDom; using SQLLinter.Common; using SQLLinter.Common.Helpers; namespace SQLLinter.Infrastructure.Rules; public class ProcedureLoggingRule : BaseRuleVisitor { public override string Text => "В процедурах обязательно логирование (@DebugLog, LABEL_FINISH): {0}"; public override void Visit(CreateProcedureStatement node) => check(node, SQLHelpers.ObjectGetFullName(node.ProcedureReference.Name)); public override void Visit(CreateOrAlterProcedureStatement node) => check(node, SQLHelpers.ObjectGetFullName(node.ProcedureReference.Name)); public override void Visit(AlterProcedureStatement node) => check(node, SQLHelpers.ObjectGetFullName(node.ProcedureReference.Name)); private void check(TSqlStatement node, string name) { var tokens = node.ScriptTokenStream; bool hasDebugLog = false; bool hasLabelFinish = false; foreach (var token in tokens) { if (token.Text is null) continue; if (token.Text.Equals("@DebugLog", StringComparison.OrdinalIgnoreCase)) { hasDebugLog = true; } else if (token.Text.Equals("LABEL_FINISH:", StringComparison.OrdinalIgnoreCase)) { hasLabelFinish = true; } } if (!hasDebugLog || !hasLabelFinish) { AddViolation(node, name); } } } public class ProcedureLoggingReturnRule : BaseRuleVisitor { public override string Text => "В процедурах с логированием RETURN запрещён: {0}"; public override void Visit(CreateProcedureStatement node) => check(node, SQLHelpers.ObjectGetFullName(node.ProcedureReference.Name)); public override void Visit(CreateOrAlterProcedureStatement node) => check(node, SQLHelpers.ObjectGetFullName(node.ProcedureReference.Name)); public override void Visit(AlterProcedureStatement node) => check(node, SQLHelpers.ObjectGetFullName(node.ProcedureReference.Name)); private void check(TSqlStatement node, string name) { var tokens = node.ScriptTokenStream; bool hasDebugLog = false; bool hasLabelFinish = false; bool hasReturn = false; List returnPositions = new(); foreach (var token in tokens) { if (token.Text is null) continue; if (token.Text.Equals("@DebugLog", StringComparison.OrdinalIgnoreCase)) { hasDebugLog = true; } else if (token.Text.Equals("LABEL_FINISH:", StringComparison.OrdinalIgnoreCase)) { hasLabelFinish = true; } else if (token.Text.Equals("RETURN", StringComparison.OrdinalIgnoreCase)) { hasReturn = true; returnPositions.Add(new(Line: token.Line, Column: token.Column)); } } if ((hasDebugLog || hasLabelFinish) || hasReturn) { returnPositions.ForEach(t => AddViolation(Name, GetText(name), t.Line, t.Column)); } } private record ReturnPosition(int Line, int Column); }