Добавьте файлы проекта.
This commit is contained in:
190
SQLLinter/Infrastructure/Rules/InformationSchemaRule.cs
Normal file
190
SQLLinter/Infrastructure/Rules/InformationSchemaRule.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using Microsoft.SqlServer.TransactSql.ScriptDom;
|
||||
using SQLLinter.Common;
|
||||
using SQLLinter.Common.Helpers;
|
||||
|
||||
namespace SQLLinter.Infrastructure.Rules;
|
||||
|
||||
public class InformationSchemaRule : BaseRuleVisitor, IRule
|
||||
{
|
||||
|
||||
public override string Text => "Ожидается использование SYS.Partitions вместо представлений INFORMATION SCHEMA.";
|
||||
|
||||
public override void Visit(SchemaObjectName node)
|
||||
{
|
||||
var schemaIdentifier = node.SchemaIdentifier?.Value != null;
|
||||
|
||||
if (schemaIdentifier && node.SchemaIdentifier.Value.Equals("INFORMATION_SCHEMA", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
AddViolation(node);
|
||||
}
|
||||
}
|
||||
|
||||
public override void FixViolation(List<string> fileLines, IRuleViolation ruleViolation, FileLineActions actions)
|
||||
{
|
||||
var node = FixHelpers.FindNodes<TSqlStatement>(fileLines, x => x.StartLine == ruleViolation.Line);
|
||||
|
||||
if (node.Count == 1)
|
||||
{
|
||||
switch (node[0])
|
||||
{
|
||||
case IfStatement ifStatement:
|
||||
HandleFragment(actions, ifStatement.Predicate);
|
||||
break;
|
||||
|
||||
case SelectStatement selectStatement:
|
||||
HandleFragment(actions, selectStatement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleFragment(FileLineActions actions, TSqlFragment statement)
|
||||
{
|
||||
var fromClauses = FixHelpers.FindNodes<FromClause>(statement);
|
||||
var whereClauses = FixHelpers.FindNodes<WhereClause>(statement);
|
||||
|
||||
if (fromClauses.Count == 1 && whereClauses.Count <= 1)
|
||||
{
|
||||
var fromClause = fromClauses[0];
|
||||
var whereClause = whereClauses.FirstOrDefault();
|
||||
var tableName = FixHelpers.FindNodes<SchemaObjectName>(fromClause)[0].BaseIdentifier.Value;
|
||||
string newFrom = null;
|
||||
|
||||
switch (tableName)
|
||||
{
|
||||
case "TABLES":
|
||||
newFrom = "FROM sys.tables";
|
||||
UpdateWhereTable(actions, fromClause, whereClause);
|
||||
|
||||
break;
|
||||
|
||||
case "ROUTINES":
|
||||
newFrom = "FROM sys.procedures";
|
||||
UpdateWhereRoutine(actions, fromClause, whereClause);
|
||||
break;
|
||||
|
||||
case "COLUMNS":
|
||||
newFrom = "FROM sys.columns";
|
||||
UpdateWhereColumns(actions, fromClause, whereClause);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
string oldFrom = FixHelpers.GetString(fromClause);
|
||||
|
||||
if (newFrom != null)
|
||||
{
|
||||
actions.RepaceInlineAt(fromClause.StartLine - 1, fromClause.StartColumn - 1,
|
||||
newFrom, oldFrom.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetWhereColumnValueName(WhereClause whereClause, string columnName)
|
||||
{
|
||||
var node = FixHelpers.FindNodes<BooleanComparisonExpression>(whereClause,
|
||||
x => FixHelpers.FindNodes<Identifier>(x.FirstExpression)[0].Value == columnName);
|
||||
|
||||
if (node.Count == 1 && node[0].SecondExpression is StringLiteral stringLiteral)
|
||||
{
|
||||
return stringLiteral.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void UpdateWhere(FileLineActions actions, FromClause fromClause, WhereClause whereClause, string newWhere)
|
||||
{
|
||||
var firstWhereLine = whereClause.ScriptTokenStream[whereClause.FirstTokenIndex].Line;
|
||||
var lastWhereLine = whereClause.ScriptTokenStream[whereClause.LastTokenIndex].Line;
|
||||
|
||||
// Delete mutliline where
|
||||
var anyDeleted = false;
|
||||
for (int line = lastWhereLine; line > firstWhereLine; line--)
|
||||
{
|
||||
actions.RemoveAt(line - 1);
|
||||
anyDeleted = true;
|
||||
}
|
||||
if (anyDeleted)
|
||||
{
|
||||
newWhere += ")";
|
||||
}
|
||||
|
||||
var oldWhere = string.Join(string.Empty, whereClause.ScriptTokenStream
|
||||
.Where((x, i) => x.Line == firstWhereLine
|
||||
&& x.Column >= whereClause.StartColumn
|
||||
&& i <= whereClause.LastTokenIndex
|
||||
&& x.Text != "\n")
|
||||
.Select(x => x.Text));
|
||||
|
||||
actions.RepaceInlineAt(whereClause.StartLine - 1, whereClause.StartColumn - 1,
|
||||
newWhere, oldWhere.Length);
|
||||
}
|
||||
|
||||
private static void UpdateWhereColumns(
|
||||
FileLineActions actions,
|
||||
FromClause fromClause,
|
||||
WhereClause whereClause)
|
||||
{
|
||||
if (whereClause != null)
|
||||
{
|
||||
var schema = GetWhereColumnValueName(whereClause, "TABLE_SCHEMA");
|
||||
var columnName = GetWhereColumnValueName(whereClause, "COLUMN_NAME");
|
||||
var tableName = GetWhereColumnValueName(whereClause, "TABLE_NAME");
|
||||
var dataType = GetWhereColumnValueName(whereClause, "DATA_TYPE");
|
||||
|
||||
if (schema != null && tableName != null && columnName != null)
|
||||
{
|
||||
var newWhere = $"WHERE [object_id] = OBJECT_ID(N'{schema}.{tableName}') AND [name] = '{columnName}'";
|
||||
if (dataType != null)
|
||||
{
|
||||
newWhere += $" AND [system_type_id] = TYPE_ID(N'{dataType}')";
|
||||
}
|
||||
|
||||
UpdateWhere(actions, fromClause, whereClause, newWhere);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateWhereRoutine(
|
||||
FileLineActions actions,
|
||||
FromClause fromClause,
|
||||
WhereClause whereClause)
|
||||
{
|
||||
if (whereClause != null)
|
||||
{
|
||||
var schema = GetWhereColumnValueName(whereClause, "ROUTINE_SCHEMA");
|
||||
var name = GetWhereColumnValueName(whereClause, "ROUTINE_NAME");
|
||||
var type = GetWhereColumnValueName(whereClause, "ROUTINE_TYPE");
|
||||
|
||||
if (type == "PROCEDURE" && schema != null && name != null)
|
||||
{
|
||||
var newWhere = $"WHERE [object_id] = OBJECT_ID(N'{schema}.{name}')";
|
||||
|
||||
UpdateWhere(actions, fromClause, whereClause, newWhere);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateWhereTable(
|
||||
FileLineActions actions,
|
||||
FromClause fromClause,
|
||||
WhereClause whereClause)
|
||||
{
|
||||
if (whereClause != null)
|
||||
{
|
||||
var schema = GetWhereColumnValueName(whereClause, "TABLE_SCHEMA");
|
||||
var name = GetWhereColumnValueName(whereClause, "TABLE_NAME");
|
||||
var type = GetWhereColumnValueName(whereClause, "TABLE_TYPE");
|
||||
|
||||
if (type == "BASE TABLE" && schema != null && name != null)
|
||||
{
|
||||
var newWhere = $"WHERE [object_id] = OBJECT_ID(N'{schema}.{name}')";
|
||||
|
||||
UpdateWhere(actions, fromClause, whereClause, newWhere);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user