Files
SQLLint/SQLLinter/Infrastructure/Rules/InformationSchemaRule.cs

191 lines
6.6 KiB
C#

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);
}
}
}
}