Добавьте файлы проекта.
This commit is contained in:
152
SQLLinter/Infrastructure/Parser/DynamicSQLParser.cs
Normal file
152
SQLLinter/Infrastructure/Parser/DynamicSQLParser.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using Microsoft.SqlServer.TransactSql.ScriptDom;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser
|
||||
{
|
||||
public class DynamicSQLParser : TSqlFragmentVisitor
|
||||
{
|
||||
private readonly Action<string, int, int> callback;
|
||||
private string executableSql = string.Empty;
|
||||
private Dictionary<string, VariableVisitor.VariableRef> VariableValues = new();
|
||||
|
||||
private int DynamicSQLStartingLine { get; set; }
|
||||
|
||||
private int DynamicSQLStartingColumn { get; set; }
|
||||
|
||||
public DynamicSQLParser(Action<string, int, int> callback)
|
||||
{
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public override void Visit(TSqlBatch node)
|
||||
{
|
||||
var variableVisitor = new VariableVisitor();
|
||||
node.Accept(variableVisitor);
|
||||
VariableValues = variableVisitor.VariableValues;
|
||||
}
|
||||
|
||||
public override void Visit(ExecuteStatement node)
|
||||
{
|
||||
DynamicSQLStartingColumn = node.ExecuteSpecification.ExecutableEntity.StartColumn;
|
||||
DynamicSQLStartingLine = node.ExecuteSpecification.ExecutableEntity.StartLine;
|
||||
|
||||
var visitor = new VariableVisitor();
|
||||
node.Accept(visitor);
|
||||
|
||||
var executableStrings = node.ExecuteSpecification.ExecutableEntity as ExecutableStringList;
|
||||
if (executableStrings?.Strings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var counter = 0;
|
||||
foreach (var executableString in executableStrings.Strings)
|
||||
{
|
||||
counter++;
|
||||
if (executableString is StringLiteral literal)
|
||||
{
|
||||
HandleLiteral(counter, executableStrings.Strings.Count, literal);
|
||||
}
|
||||
else if (executableString is VariableReference variableReference)
|
||||
{
|
||||
HandleVariable(counter, executableStrings.Strings.Count, variableReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleVariable(int counter, int executableCount, VariableReference variableReference)
|
||||
{
|
||||
if (!VariableValues.ContainsKey(variableReference.Name) || !VariableValues.TryGetValue(variableReference.Name, out var value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
executableSql += value.Value;
|
||||
|
||||
if (counter == executableCount)
|
||||
{
|
||||
callback(executableSql, value.StartLine, value.StartColumn);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLiteral(int counter, int executableCount, StringLiteral literal)
|
||||
{
|
||||
executableSql += literal.Value;
|
||||
if (counter == executableCount)
|
||||
{
|
||||
callback(executableSql, DynamicSQLStartingLine, DynamicSQLStartingColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class VariableVisitor : TSqlFragmentVisitor
|
||||
{
|
||||
public Dictionary<string, VariableRef> VariableValues { get; } = new();
|
||||
|
||||
public override void Visit(SelectSetVariable node)
|
||||
{
|
||||
HandleExpression(node.Variable.Name, node.Expression);
|
||||
}
|
||||
|
||||
public override void Visit(SetVariableStatement node)
|
||||
{
|
||||
HandleExpression(node.Variable.Name, node.Expression);
|
||||
}
|
||||
|
||||
private void HandleExpression(string name, ScalarExpression expression)
|
||||
{
|
||||
switch (expression)
|
||||
{
|
||||
case StringLiteral strLiteral:
|
||||
VariableValues[name] = new VariableRef(strLiteral);
|
||||
break;
|
||||
case IntegerLiteral intLiteral:
|
||||
VariableValues[name] = new VariableRef(intLiteral);
|
||||
break;
|
||||
case BinaryExpression binaryExpression:
|
||||
HandleBinaryExpression(name, binaryExpression);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBinaryExpression(string name, BinaryExpression expression)
|
||||
{
|
||||
if (expression.BinaryExpressionType != BinaryExpressionType.Add)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (expression.FirstExpression is StringLiteral first
|
||||
&& expression.SecondExpression is StringLiteral second)
|
||||
{
|
||||
VariableValues[name] = new VariableRef(first)
|
||||
{
|
||||
Value = first.Value + second.Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public struct VariableRef
|
||||
{
|
||||
public VariableRef(StringLiteral stringLiteral)
|
||||
: this((Literal)stringLiteral)
|
||||
{
|
||||
}
|
||||
|
||||
public VariableRef(IntegerLiteral integerLiteral)
|
||||
: this((Literal)integerLiteral)
|
||||
{
|
||||
}
|
||||
|
||||
private VariableRef(Literal literal)
|
||||
{
|
||||
StartColumn = literal.StartColumn;
|
||||
StartLine = literal.StartLine;
|
||||
Value = literal.Value;
|
||||
}
|
||||
|
||||
public int StartColumn { get; set; }
|
||||
public int StartLine { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
39
SQLLinter/Infrastructure/Parser/FileSystemWrapper.cs
Normal file
39
SQLLinter/Infrastructure/Parser/FileSystemWrapper.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using SQLLinter.Core.Interfaces;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser
|
||||
{
|
||||
public class FileSystemWrapper : IFileSystemWrapper
|
||||
{
|
||||
|
||||
public bool FileExists(string path)
|
||||
{
|
||||
path = RemoveQuotes(path);
|
||||
return File.Exists(path);
|
||||
}
|
||||
|
||||
public bool PathIsValidForLint(string path)
|
||||
{
|
||||
path = RemoveQuotes(path);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return Directory.Exists(path) || PathContainsWildCard(path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool PathContainsWildCard(string filePath)
|
||||
{
|
||||
return filePath.Contains("*") || filePath.Contains("?");
|
||||
}
|
||||
|
||||
private string RemoveQuotes(string path)
|
||||
{
|
||||
return path.Replace("\"", string.Empty);
|
||||
}
|
||||
|
||||
public string CombinePath(params string[] paths)
|
||||
{
|
||||
return Path.Combine(paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
SQLLinter/Infrastructure/Parser/FragmentBuilder.cs
Normal file
84
SQLLinter/Infrastructure/Parser/FragmentBuilder.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using Microsoft.SqlServer.TransactSql.ScriptDom;
|
||||
using SQLLinter.Common;
|
||||
using SQLLinter.Core;
|
||||
using SQLLinter.Core.Interfaces;
|
||||
using SQLLinter.Infrastructure.Configuration.Overrides;
|
||||
using SQLLinter.Infrastructure.Interfaces;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser;
|
||||
|
||||
public class FragmentBuilder : IFragmentBuilder
|
||||
{
|
||||
private readonly TSqlParser parser;
|
||||
private readonly IReporter _reporter;
|
||||
|
||||
public FragmentBuilder(IReporter reporter) : this(reporter, Constants.DefaultCompatabilityLevel)
|
||||
{
|
||||
}
|
||||
|
||||
public FragmentBuilder(IReporter reporter, int compatabilityLevel)
|
||||
{
|
||||
parser = GetSqlParser(compatabilityLevel);
|
||||
_reporter = reporter;
|
||||
}
|
||||
|
||||
public TSqlFragment? GetFragment(string path, TextReader txtRdr, out IList<ParseError> errors, IEnumerable<IOverride> overrides = null)
|
||||
{
|
||||
TSqlFragment fragment;
|
||||
|
||||
OverrideCompatabilityLevel? compatibilityLevel = null;
|
||||
if (overrides != null)
|
||||
{
|
||||
foreach (var lintingOverride in overrides)
|
||||
{
|
||||
if (lintingOverride is OverrideCompatabilityLevel overrideCompatability)
|
||||
{
|
||||
compatibilityLevel = overrideCompatability;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TSqlParser curParser;
|
||||
|
||||
if (compatibilityLevel != null)
|
||||
{
|
||||
curParser = GetSqlParser(compatibilityLevel.CompatabilityLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
curParser = parser;
|
||||
}
|
||||
|
||||
fragment = curParser.Parse(txtRdr, out errors); //TODO: Возвращать эти ошибки
|
||||
|
||||
if (fragment == null)
|
||||
{
|
||||
foreach (var err in errors)
|
||||
{
|
||||
_reporter.ReportViolation(path, err.Line, err.Column, RuleViolationSeverity.Critical, "parse", err.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return fragment?.FirstTokenIndex != -1 ? fragment : null;
|
||||
}
|
||||
|
||||
private static TSqlParser GetSqlParser(int compatabilityLevel)
|
||||
{
|
||||
TSqlParser parser = compatabilityLevel switch
|
||||
{
|
||||
80 => new TSql80Parser(true),
|
||||
90 => new TSql90Parser(true),
|
||||
100 => new TSql100Parser(true),
|
||||
110 => new TSql110Parser(true),
|
||||
120 => new TSql120Parser(true),
|
||||
130 => new TSql130Parser(true),
|
||||
140 => new TSql140Parser(true),
|
||||
150 => new TSql150Parser(true),
|
||||
160 => new TSql160Parser(true),
|
||||
170 => new TSql170Parser(true),
|
||||
_ => new TSql120Parser(true),
|
||||
};
|
||||
|
||||
return parser;
|
||||
}
|
||||
}
|
||||
23
SQLLinter/Infrastructure/Parser/ParsingUtility.cs
Normal file
23
SQLLinter/Infrastructure/Parser/ParsingUtility.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Text;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser
|
||||
{
|
||||
public static class ParsingUtility
|
||||
{
|
||||
public static TextReader CreateTextReaderFromString(string str)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(str);
|
||||
return new StreamReader(new MemoryStream(bytes));
|
||||
}
|
||||
|
||||
public static Stream GenerateStreamFromString(string s)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var writer = new StreamWriter(stream);
|
||||
writer.Write(s);
|
||||
writer.Flush();
|
||||
stream.Position = 0;
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using SQLLinter.Common;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser
|
||||
{
|
||||
public static class RuleVisitorFriendlyNameTypeMap
|
||||
{
|
||||
public static List<Type> DefaultRuleTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
return GetRuleTypes(assembly);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Type> GetRuleTypes(Assembly assembly)
|
||||
{
|
||||
List<Type> sqlRuleTypes = assembly
|
||||
.GetTypes()
|
||||
.Where(t => typeof(IRule).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract) // исключаем абстрактные
|
||||
.ToList();
|
||||
|
||||
return sqlRuleTypes;
|
||||
}
|
||||
}
|
||||
}
|
||||
109
SQLLinter/Infrastructure/Parser/SqlFileProcessor.cs
Normal file
109
SQLLinter/Infrastructure/Parser/SqlFileProcessor.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using SQLLinter.Common;
|
||||
using SQLLinter.Core.Interfaces;
|
||||
using SQLLinter.Infrastructure.Rules.RuleExceptions;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser;
|
||||
|
||||
public class SqlFileProcessor : ISqlFileProcessor
|
||||
{
|
||||
private readonly IRuleVisitor ruleVisitor;
|
||||
|
||||
private readonly IReporter reporter;
|
||||
|
||||
private readonly IPluginHandler pluginHandler;
|
||||
|
||||
private readonly IRuleExceptionFinder ruleExceptionFinder;
|
||||
|
||||
public SqlFileProcessor(
|
||||
IRuleVisitor ruleVisitor,
|
||||
IPluginHandler pluginHandler,
|
||||
IReporter reporter)
|
||||
{
|
||||
this.ruleVisitor = ruleVisitor;
|
||||
this.pluginHandler = pluginHandler;
|
||||
this.reporter = reporter;
|
||||
ruleExceptionFinder = new RuleExceptionFinder(pluginHandler.RuleWithNames);
|
||||
}
|
||||
|
||||
private int _fileCount;
|
||||
public int FileCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fileCount;
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessList(List<string> filePaths)
|
||||
{
|
||||
foreach (var path in filePaths)
|
||||
{
|
||||
ProcessFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessPath(string path)
|
||||
{
|
||||
ProcessFile(path);
|
||||
}
|
||||
|
||||
private void ProcessFile(string filePath)
|
||||
{
|
||||
var fileStream = GetFileContents(filePath);
|
||||
HandleProcessing(filePath, fileStream);
|
||||
}
|
||||
|
||||
public void ProcessList(Dictionary<string, Stream> files)
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
HandleProcessing(file.Key, file.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsWholeFileIgnored(string filePath, IEnumerable<IExtendedRuleException> ignoredRules)
|
||||
{
|
||||
var ignoredRulesEnum = ignoredRules.ToArray();
|
||||
if (!ignoredRulesEnum.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var lineOneRuleIgnores = ignoredRulesEnum.OfType<GlobalRuleException>().Where(x => 1 == x.StartLine).ToArray();
|
||||
if (!lineOneRuleIgnores.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var lineCount = 0;
|
||||
using (var reader = new StreamReader(GetFileContents(filePath)))
|
||||
{
|
||||
while (reader.ReadLine() != null)
|
||||
{
|
||||
lineCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return lineOneRuleIgnores.Any(x => x.EndLine == lineCount);
|
||||
}
|
||||
|
||||
private void HandleProcessing(string filePath, Stream fileStream)
|
||||
{
|
||||
var ignoredRules = ruleExceptionFinder.GetIgnoredRuleList(fileStream).ToList();
|
||||
if (IsWholeFileIgnored(filePath, ignoredRules))
|
||||
{
|
||||
return;
|
||||
}
|
||||
ProcessRules(fileStream, ignoredRules, filePath);
|
||||
}
|
||||
|
||||
private void ProcessRules(Stream fileStream, IEnumerable<IRuleException> ignoredRules, string filePath)
|
||||
{
|
||||
ruleVisitor.VisitRules(filePath, ignoredRules, fileStream);
|
||||
}
|
||||
|
||||
private Stream GetFileContents(string filePath)
|
||||
{
|
||||
return File.OpenRead(filePath);
|
||||
}
|
||||
}
|
||||
123
SQLLinter/Infrastructure/Parser/SqlRuleVisitor.cs
Normal file
123
SQLLinter/Infrastructure/Parser/SqlRuleVisitor.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using Microsoft.SqlServer.TransactSql.ScriptDom;
|
||||
using SQLLinter.Common;
|
||||
using SQLLinter.Core.Interfaces;
|
||||
using SQLLinter.Infrastructure.Configuration.Overrides;
|
||||
using SQLLinter.Infrastructure.Interfaces;
|
||||
using SQLLinter.Infrastructure.Rules.RuleExceptions;
|
||||
using SQLLinter.Infrastructure.Rules.RuleViolations;
|
||||
using System.Data;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser;
|
||||
|
||||
public class SqlRuleVisitor : IRuleVisitor
|
||||
{
|
||||
private readonly IFragmentBuilder _fragmentBuilder;
|
||||
private readonly IReporter _reporter;
|
||||
private readonly ISqlStreamReaderBuilder _sqlStreamReaderBuilder;
|
||||
private readonly IPluginHandler _pluginHandler;
|
||||
|
||||
private readonly OverrideFinder _overrideFinder = new OverrideFinder();
|
||||
|
||||
public SqlRuleVisitor(IPluginHandler pluginHandler, IFragmentBuilder fragmentBuilder, IReporter reporter)
|
||||
: this(pluginHandler, fragmentBuilder, reporter, new SqlStreamReaderBuilder()) { }
|
||||
|
||||
public SqlRuleVisitor(IPluginHandler pluginHandler, IFragmentBuilder fragmentBuilder, IReporter reporter, ISqlStreamReaderBuilder sqlStreamReaderBuilder)
|
||||
{
|
||||
this._fragmentBuilder = fragmentBuilder;
|
||||
this._reporter = reporter;
|
||||
this._pluginHandler = pluginHandler;
|
||||
this._sqlStreamReaderBuilder = sqlStreamReaderBuilder;
|
||||
}
|
||||
|
||||
public void VisitRules(string sqlPath, IEnumerable<IRuleException> ignoredRules, Stream sqlFileStream)
|
||||
{
|
||||
var overrides = _overrideFinder.GetOverrideList(sqlFileStream);
|
||||
var overrideArray = overrides as IOverride[] ?? overrides.ToArray();
|
||||
|
||||
var sqlFragment = _fragmentBuilder.GetFragment(sqlPath, GetSqlTextReader(sqlFileStream), out var errors, overrideArray);
|
||||
|
||||
if (sqlFragment == null) return;
|
||||
|
||||
var ruleExceptions = ignoredRules as IRuleException[] ?? ignoredRules.ToArray();
|
||||
if (errors.Any())
|
||||
{
|
||||
HandleParserErrors(sqlPath, errors, ruleExceptions);
|
||||
}
|
||||
|
||||
var rules = _pluginHandler.Rules;
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
VisitFragment(sqlFragment, rule, overrideArray, sqlPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void VisitFragment(TSqlFragment sqlFragment, IRule rule, IEnumerable<IOverride> overrides, string filePath)
|
||||
{
|
||||
var violations = rule.Analyze(sqlFragment).ToList();
|
||||
|
||||
|
||||
if (!VisitorIsBlackListedForDynamicSql(rule))
|
||||
{
|
||||
var dynamicSqlVisitor = new DynamicSQLParser(DynamicSqlCallback);
|
||||
sqlFragment?.Accept(dynamicSqlVisitor);
|
||||
}
|
||||
|
||||
void DynamicSqlCallback(string dynamicSQL, int DynamicSqlStartLine, int DynamicSqlStartColumn)
|
||||
{
|
||||
rule.DynamicSqlStartLine = DynamicSqlStartLine;
|
||||
rule.DynamicSqlStartColumn = DynamicSqlStartColumn;
|
||||
|
||||
var dynamicSqlStream = ParsingUtility.GenerateStreamFromString(dynamicSQL);
|
||||
var dynamicFragment = _fragmentBuilder.GetFragment(filePath, GetSqlTextReader(dynamicSqlStream), out var errors, overrides);
|
||||
|
||||
if (dynamicFragment != null)
|
||||
{
|
||||
violations.AddRange(rule.Analyze(dynamicFragment));
|
||||
}
|
||||
}
|
||||
|
||||
violations.ForEach(t => _reporter.ReportViolation(filePath, t.Line, t.Column, rule.Severity, t.RuleName, t.Message));
|
||||
}
|
||||
|
||||
private static bool VisitorIsBlackListedForDynamicSql(IRule visitor)
|
||||
{
|
||||
return new List<string>
|
||||
{
|
||||
"SetAnsiNullsRule",
|
||||
"SetNoCountRule",
|
||||
"SetQuotedIdentifierRule",
|
||||
"SetTransactionIsolationLevelRule",
|
||||
"UnicodeStringRule"
|
||||
}.Any(x => visitor.GetType().ToString().Contains(x));
|
||||
}
|
||||
|
||||
private StreamReader GetSqlTextReader(Stream sqlFileStream)
|
||||
{
|
||||
return _sqlStreamReaderBuilder.CreateReader(sqlFileStream);
|
||||
}
|
||||
|
||||
private void HandleParserErrors(string sqlPath, IEnumerable<ParseError> errors, IEnumerable<IRuleException> ignoredRules)
|
||||
{
|
||||
var updatedExitCode = false;
|
||||
var ruleExceptions = ignoredRules as IRuleException[] ?? ignoredRules.ToArray();
|
||||
|
||||
foreach (var error in errors)
|
||||
{
|
||||
var globalRulesOnLine = ruleExceptions.OfType<GlobalRuleException>().Where(
|
||||
x => error.Line >= x.StartLine
|
||||
&& error.Line <= x.EndLine);
|
||||
|
||||
if (!globalRulesOnLine.Any())
|
||||
{
|
||||
_reporter.ReportViolation(new RuleViolation(sqlPath, "invalid-syntax", error.Message, error.Line, error.Column, RuleViolationSeverity.Critical));
|
||||
if (updatedExitCode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
updatedExitCode = true;
|
||||
Environment.ExitCode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
SQLLinter/Infrastructure/Parser/SqlStreamReaderBuilder.cs
Normal file
44
SQLLinter/Infrastructure/Parser/SqlStreamReaderBuilder.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using SQLLinter.Infrastructure.Interfaces;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser;
|
||||
|
||||
public class SqlStreamReaderBuilder : ISqlStreamReaderBuilder
|
||||
{
|
||||
private static readonly Regex _placeholderRegex = new Regex(@"\$\((?<placeholder>[^)]+)\)", RegexOptions.Compiled);
|
||||
|
||||
public StreamReader CreateReader(Stream sqlFileStream)
|
||||
{
|
||||
var sqlText = new StreamReader(sqlFileStream);
|
||||
sqlFileStream.Seek(0, SeekOrigin.Begin);
|
||||
var sql = ReplaceSqlPlaceholders(sqlText.ReadToEnd());
|
||||
return new StreamReader(new MemoryStream(sqlText.CurrentEncoding.GetBytes(sql)));
|
||||
}
|
||||
|
||||
private string ReplaceSqlPlaceholders(string sql)
|
||||
{
|
||||
var matches = _placeholderRegex.Matches(sql);
|
||||
|
||||
if (matches.Count == 0)
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
var newSql = new StringBuilder();
|
||||
var i = 0;
|
||||
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
var placeholder = match.Groups["placeholder"].Value;
|
||||
var replacement = match.Value;
|
||||
newSql.Append(sql.Substring(i, match.Index - i));
|
||||
newSql.Append(replacement);
|
||||
i = match.Index + match.Length;
|
||||
}
|
||||
|
||||
newSql.Append(sql.Substring(i));
|
||||
|
||||
return newSql.ToString();
|
||||
}
|
||||
}
|
||||
50
SQLLinter/Infrastructure/Parser/ViolationFixer.cs
Normal file
50
SQLLinter/Infrastructure/Parser/ViolationFixer.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using SQLLinter.Common;
|
||||
using SQLLinter.Infrastructure.Interfaces;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Parser;
|
||||
|
||||
public class ViolationFixer : IViolationFixer
|
||||
{
|
||||
private readonly Dictionary<string, IRule> Rules;
|
||||
private readonly IList<IRuleViolation> Violations;
|
||||
|
||||
public ViolationFixer(
|
||||
Dictionary<string, IRule> rules,
|
||||
IList<IRuleViolation> violations)
|
||||
{
|
||||
Rules = rules;
|
||||
Violations = violations;
|
||||
}
|
||||
|
||||
public void Fix()
|
||||
{
|
||||
var files = Violations.GroupBy(x => x.FileName);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var fileViolations = file
|
||||
.OrderByDescending(x => x.Line)
|
||||
.ThenByDescending(x => x.Column)
|
||||
.ToList();
|
||||
|
||||
var fileLines = File.ReadAllLines(file.Key).ToList();
|
||||
var fileLineActions = new Common.FileLineActions(fileViolations, fileLines);
|
||||
|
||||
foreach (var violation in fileViolations)
|
||||
{
|
||||
if (Rules.ContainsKey(violation.RuleName))
|
||||
{
|
||||
if (violation.Line == 1 && violation.Column > fileLines[violation.Line - 1].Length + 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var lines = new List<string>(fileLines);
|
||||
//Rules[violation.RuleName].FixViolation(lines, violation, fileLineActions);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(file.Key, fileLines);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user