Merge pull request 'refactor(breakdowns): Extract shared collection analysis logic' (#7) from fix/sonarqube-duplicate-code-lines into main
SonarQube Analysis / sonarqube (push) Successful in 3m27s
SonarQube Analysis / sonarqube (push) Successful in 3m27s
Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
@@ -163,35 +163,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Summary information for each query.</returns>
|
/// <returns>Summary information for each query.</returns>
|
||||||
public IEnumerable<SqlServer.QuerySummary> GetQuerySummaries()
|
public IEnumerable<SqlServer.QuerySummary> GetQuerySummaries()
|
||||||
{
|
=> SqlServer.QueryCollectionAnalysisHelper.GetQuerySummaries(_queryBreakdowns);
|
||||||
return _queryBreakdowns.Select((q, index) => new SqlServer.QuerySummary
|
|
||||||
{
|
|
||||||
Index = index,
|
|
||||||
HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
|
|
||||||
HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
|
|
||||||
HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
|
|
||||||
HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
|
|
||||||
HasHavingClause = !string.IsNullOrWhiteSpace(q.HavingClause?.Clause),
|
|
||||||
HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
|
|
||||||
HasJoins = false,
|
|
||||||
HasCTE = q.WithClauses.Count > 0,
|
|
||||||
ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
|
|
||||||
ParameterCount = q.ParameterList.Count(),
|
|
||||||
JoinCount = 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the total number of selected columns across all queries.
|
/// Gets the total number of selected columns across all queries.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Total column count.</returns>
|
/// <returns>Total column count.</returns>
|
||||||
public int GetTotalSelectedColumns()
|
public int GetTotalSelectedColumns()
|
||||||
{
|
=> SqlServer.QueryCollectionAnalysisHelper.GetTotalSelectedColumns(_queryBreakdowns);
|
||||||
return _queryBreakdowns.Sum(q =>
|
|
||||||
!string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
|
|
||||||
? q.SelectClause.Clause.Split(',').Length
|
|
||||||
: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all unique table names referenced across all queries.
|
/// Gets all unique table names referenced across all queries.
|
||||||
@@ -203,20 +182,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>List of unique table names.</returns>
|
/// <returns>List of unique table names.</returns>
|
||||||
public IEnumerable<string> GetUniqueTableReferences()
|
public IEnumerable<string> GetUniqueTableReferences()
|
||||||
{
|
=> SqlServer.QueryCollectionAnalysisHelper.GetUniqueTableReferences(_queryBreakdowns);
|
||||||
var tables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var tableNames = _queryBreakdowns
|
|
||||||
.Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
|
|
||||||
.SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
|
|
||||||
|
|
||||||
foreach (var table in tableNames)
|
|
||||||
{
|
|
||||||
tables.Add(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets parameter usage information across all queries.
|
/// Gets parameter usage information across all queries.
|
||||||
@@ -224,83 +190,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// <returns>Parameter usage information.</returns>
|
/// <returns>Parameter usage information.</returns>
|
||||||
public IEnumerable<ParameterUsageReport> GetParameterUsageReport()
|
public IEnumerable<ParameterUsageReport> GetParameterUsageReport()
|
||||||
{
|
{
|
||||||
// Collect all unique parameter names from both ParameterList and Parameters dictionary
|
return SqlServer.QueryCollectionAnalysisHelper.GetParameterUsage(_queryBreakdowns)
|
||||||
var allParamNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
.Select(usage => new ParameterUsageReport
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Add from ParameterList (parsed parameters)
|
|
||||||
foreach (var param in query.ParameterList)
|
|
||||||
{
|
{
|
||||||
allParamNames.Add(param.Name);
|
ParameterName = usage.Name,
|
||||||
}
|
Value = usage.Value,
|
||||||
|
UsedInQueryCount = usage.UsedInQueryCount,
|
||||||
// Add from Parameters dictionary (manually added parameters)
|
TotalQueries = usage.TotalQueries
|
||||||
foreach (var paramName in query.Parameters.Keys)
|
});
|
||||||
{
|
|
||||||
allParamNames.Add(paramName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var paramName in allParamNames)
|
|
||||||
{
|
|
||||||
var queriesUsing = 0;
|
|
||||||
object? lastValue = null;
|
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Check ParameterList first (parsed)
|
|
||||||
var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
if (param != null)
|
|
||||||
{
|
|
||||||
queriesUsing++;
|
|
||||||
lastValue = param.Value;
|
|
||||||
}
|
|
||||||
// Also check Parameters dictionary (manually added)
|
|
||||||
else if (query.Parameters.TryGetValue(paramName, out var dictValue))
|
|
||||||
{
|
|
||||||
queriesUsing++;
|
|
||||||
lastValue = dictValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return new ParameterUsageReport
|
|
||||||
{
|
|
||||||
ParameterName = paramName,
|
|
||||||
Value = lastValue,
|
|
||||||
UsedInQueryCount = queriesUsing,
|
|
||||||
TotalQueries = _queryBreakdowns.Count
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method to extract table names from a FROM clause.
|
|
||||||
/// </summary>
|
|
||||||
private static IEnumerable<string> ExtractTableNames(string fromClause)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(fromClause))
|
|
||||||
{
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple extraction: split by comma and clean up aliases
|
|
||||||
var parts = fromClause.Split(',');
|
|
||||||
foreach (var part in parts)
|
|
||||||
{
|
|
||||||
var trimmed = part.Trim();
|
|
||||||
|
|
||||||
// Remove alias (assuming format: table AS alias or table alias)
|
|
||||||
var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (tokens.Length > 0)
|
|
||||||
{
|
|
||||||
var tableName = tokens[0].Trim();
|
|
||||||
if (!string.IsNullOrWhiteSpace(tableName))
|
|
||||||
{
|
|
||||||
yield return tableName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -474,44 +474,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// Ensures that if a parameter with the same name exists in multiple queries, they all have the same value.
|
/// Ensures that if a parameter with the same name exists in multiple queries, they all have the same value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SynchronizeParameters()
|
public void SynchronizeParameters()
|
||||||
{
|
=> SqlServer.QueryCollectionAnalysisHelper.SynchronizeParameters(_queryBreakdowns);
|
||||||
// Get all unique parameter names across all queries
|
|
||||||
var allParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
foreach (var paramName in query.Parameters.Keys)
|
|
||||||
{
|
|
||||||
allParameterNames.Add(paramName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each parameter, use the last query's value and sync to all queries that have it
|
|
||||||
foreach (var paramName in allParameterNames)
|
|
||||||
{
|
|
||||||
object? lastValue = null;
|
|
||||||
bool parameterFound = false;
|
|
||||||
|
|
||||||
// Find the last query that has this parameter and get its value
|
|
||||||
for (int i = _queryBreakdowns.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (_queryBreakdowns[i].Parameters.ContainsKey(paramName))
|
|
||||||
{
|
|
||||||
lastValue = _queryBreakdowns[i].Parameters[paramName];
|
|
||||||
parameterFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synchronize the parameter value to all queries that have it
|
|
||||||
if (parameterFound)
|
|
||||||
{
|
|
||||||
foreach (var query in _queryBreakdowns.Where(q => q.Parameters.ContainsKey(paramName)))
|
|
||||||
{
|
|
||||||
query.Parameters[paramName] = lastValue!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a parameter with a specific value to all queries in the collection.
|
/// Adds a parameter with a specific value to all queries in the collection.
|
||||||
@@ -536,26 +499,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A dictionary containing all unique parameters across all queries.</returns>
|
/// <returns>A dictionary containing all unique parameters across all queries.</returns>
|
||||||
protected Dictionary<string, object?> GetCombinedParameterDictionary()
|
protected Dictionary<string, object?> GetCombinedParameterDictionary()
|
||||||
{
|
=> SqlServer.QueryCollectionAnalysisHelper.GetCombinedParameters(_queryBreakdowns);
|
||||||
var combinedParams = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Add from ParameterList (parsed parameters)
|
|
||||||
foreach (var param in query.ParameterList)
|
|
||||||
{
|
|
||||||
combinedParams[param.Name] = param.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add/override from Parameters dictionary (manually added parameters)
|
|
||||||
foreach (var param in query.Parameters)
|
|
||||||
{
|
|
||||||
combinedParams[param.Key] = param.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinedParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a formatted string representation of all unique parameters with Snowflake-specific syntax.
|
/// Gets a formatted string representation of all unique parameters with Snowflake-specific syntax.
|
||||||
@@ -607,54 +551,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// <returns>Parameter usage information.</returns>
|
/// <returns>Parameter usage information.</returns>
|
||||||
public IEnumerable<ParameterUsageReport> GetParameterUsageReport()
|
public IEnumerable<ParameterUsageReport> GetParameterUsageReport()
|
||||||
{
|
{
|
||||||
// Collect all unique parameter names from both ParameterList and Parameters dictionary
|
return SqlServer.QueryCollectionAnalysisHelper.GetParameterUsage(_queryBreakdowns)
|
||||||
var allParamNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
.Select(usage => new ParameterUsageReport
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Add from ParameterList (parsed parameters)
|
|
||||||
foreach (var param in query.ParameterList)
|
|
||||||
{
|
{
|
||||||
allParamNames.Add(param.Name);
|
ParameterName = usage.Name,
|
||||||
}
|
Value = usage.Value,
|
||||||
|
UsedInQueryCount = usage.UsedInQueryCount,
|
||||||
// Add from Parameters dictionary (manually added parameters)
|
TotalQueries = usage.TotalQueries
|
||||||
foreach (var paramName in query.Parameters.Keys)
|
});
|
||||||
{
|
|
||||||
allParamNames.Add(paramName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var paramName in allParamNames)
|
|
||||||
{
|
|
||||||
var queriesUsing = 0;
|
|
||||||
object? lastValue = null;
|
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Check ParameterList first (parsed)
|
|
||||||
var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
if (param != null)
|
|
||||||
{
|
|
||||||
queriesUsing++;
|
|
||||||
lastValue = param.Value;
|
|
||||||
}
|
|
||||||
// Also check Parameters dictionary (manually added)
|
|
||||||
else if (query.Parameters.TryGetValue(paramName, out var dictValue))
|
|
||||||
{
|
|
||||||
queriesUsing++;
|
|
||||||
lastValue = dictValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return new ParameterUsageReport
|
|
||||||
{
|
|
||||||
ParameterName = paramName,
|
|
||||||
Value = lastValue,
|
|
||||||
UsedInQueryCount = queriesUsing,
|
|
||||||
TotalQueries = _queryBreakdowns.Count
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -662,12 +566,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Total column count.</returns>
|
/// <returns>Total column count.</returns>
|
||||||
public int GetTotalSelectedColumns()
|
public int GetTotalSelectedColumns()
|
||||||
{
|
=> SqlServer.QueryCollectionAnalysisHelper.GetTotalSelectedColumns(_queryBreakdowns);
|
||||||
return _queryBreakdowns.Sum(q =>
|
|
||||||
!string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
|
|
||||||
? q.SelectClause.Clause.Split(',').Length
|
|
||||||
: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all unique table names referenced across all queries.
|
/// Gets all unique table names referenced across all queries.
|
||||||
@@ -679,20 +578,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>List of unique table names.</returns>
|
/// <returns>List of unique table names.</returns>
|
||||||
public IEnumerable<string> GetUniqueTableReferences()
|
public IEnumerable<string> GetUniqueTableReferences()
|
||||||
{
|
=> SqlServer.QueryCollectionAnalysisHelper.GetUniqueTableReferences(_queryBreakdowns);
|
||||||
var tables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var tableNames = _queryBreakdowns
|
|
||||||
.Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
|
|
||||||
.SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
|
|
||||||
|
|
||||||
foreach (var table in tableNames)
|
|
||||||
{
|
|
||||||
tables.Add(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a summary of all queries including their types and basic composition.
|
/// Gets a summary of all queries including their types and basic composition.
|
||||||
@@ -722,32 +608,6 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method to extract table names from a FROM clause.
|
|
||||||
/// </summary>
|
|
||||||
private static IEnumerable<string> ExtractTableNames(string fromClause)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(fromClause))
|
|
||||||
{
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parts = fromClause.Split(',');
|
|
||||||
foreach (var part in parts)
|
|
||||||
{
|
|
||||||
var trimmed = part.Trim();
|
|
||||||
var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (tokens.Length > 0)
|
|
||||||
{
|
|
||||||
var tableName = tokens[0].Trim();
|
|
||||||
if (!string.IsNullOrWhiteSpace(tableName))
|
|
||||||
{
|
|
||||||
yield return tableName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper method to convert a .NET object to its corresponding Snowflake data type string.
|
/// Helper method to convert a .NET object to its corresponding Snowflake data type string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -257,12 +257,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Total column count.</returns>
|
/// <returns>Total column count.</returns>
|
||||||
public int GetTotalSelectedColumns()
|
public int GetTotalSelectedColumns()
|
||||||
{
|
=> QueryCollectionAnalysisHelper.GetTotalSelectedColumns(_queryBreakdowns);
|
||||||
return _queryBreakdowns.Sum(q =>
|
|
||||||
!string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
|
|
||||||
? q.SelectClause.Clause.Split(',').Length
|
|
||||||
: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all unique table names referenced across all queries.
|
/// Gets all unique table names referenced across all queries.
|
||||||
@@ -274,72 +269,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>List of unique table names.</returns>
|
/// <returns>List of unique table names.</returns>
|
||||||
public IEnumerable<string> GetUniqueTableReferences()
|
public IEnumerable<string> GetUniqueTableReferences()
|
||||||
{
|
=> QueryCollectionAnalysisHelper.GetUniqueTableReferences(_queryBreakdowns);
|
||||||
var tables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var tableNames = _queryBreakdowns
|
|
||||||
.Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
|
|
||||||
.SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
|
|
||||||
|
|
||||||
foreach (var table in tableNames)
|
|
||||||
{
|
|
||||||
tables.Add(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a summary of all queries including their types and basic composition.
|
/// Gets a summary of all queries including their types and basic composition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Summary information for each query.</returns>
|
/// <returns>Summary information for each query.</returns>
|
||||||
public IEnumerable<QuerySummary> GetQuerySummaries()
|
public IEnumerable<QuerySummary> GetQuerySummaries()
|
||||||
{
|
=> QueryCollectionAnalysisHelper.GetQuerySummaries(_queryBreakdowns);
|
||||||
return _queryBreakdowns.Select((q, index) => new QuerySummary
|
|
||||||
{
|
|
||||||
Index = index,
|
|
||||||
HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
|
|
||||||
HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
|
|
||||||
HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
|
|
||||||
HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
|
|
||||||
HasHavingClause = !string.IsNullOrWhiteSpace(q.HavingClause?.Clause),
|
|
||||||
HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
|
|
||||||
HasJoins = false,
|
|
||||||
HasCTE = q.WithClauses.Count > 0,
|
|
||||||
ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
|
|
||||||
ParameterCount = q.ParameterList.Count(),
|
|
||||||
JoinCount = 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method to extract table names from a FROM clause.
|
|
||||||
/// </summary>
|
|
||||||
private static IEnumerable<string> ExtractTableNames(string fromClause)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(fromClause))
|
|
||||||
{
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple extraction: split by comma and clean up aliases
|
|
||||||
var parts = fromClause.Split(',');
|
|
||||||
foreach (var part in parts)
|
|
||||||
{
|
|
||||||
var trimmed = part.Trim();
|
|
||||||
|
|
||||||
// Remove alias (assuming format: table AS alias or table alias)
|
|
||||||
var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (tokens.Length > 0)
|
|
||||||
{
|
|
||||||
var tableName = tokens[0].Trim();
|
|
||||||
if (!string.IsNullOrWhiteSpace(tableName))
|
|
||||||
{
|
|
||||||
yield return tableName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronizes parameters across all queries in the collection.
|
/// Synchronizes parameters across all queries in the collection.
|
||||||
@@ -350,44 +287,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// Only synchronizes parameters that the query already defines to avoid adding unused parameters.
|
/// Only synchronizes parameters that the query already defines to avoid adding unused parameters.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void SynchronizeParameters()
|
public void SynchronizeParameters()
|
||||||
{
|
=> QueryCollectionAnalysisHelper.SynchronizeParameters(_queryBreakdowns);
|
||||||
// Get all unique parameter names across all queries
|
|
||||||
var allParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
foreach (var paramName in query.Parameters.Keys)
|
|
||||||
{
|
|
||||||
allParameterNames.Add(paramName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each parameter, use the last query's value and sync to all queries that have it
|
|
||||||
foreach (var paramName in allParameterNames)
|
|
||||||
{
|
|
||||||
object? lastValue = null;
|
|
||||||
bool parameterFound = false;
|
|
||||||
|
|
||||||
// Find the last query that has this parameter and get its value
|
|
||||||
for (int i = _queryBreakdowns.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (_queryBreakdowns[i].Parameters.ContainsKey(paramName))
|
|
||||||
{
|
|
||||||
lastValue = _queryBreakdowns[i].Parameters[paramName];
|
|
||||||
parameterFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synchronize the parameter value to all queries that have it
|
|
||||||
if (parameterFound)
|
|
||||||
{
|
|
||||||
foreach (var query in _queryBreakdowns.Where(q => q.Parameters.ContainsKey(paramName)))
|
|
||||||
{
|
|
||||||
query.Parameters[paramName] = lastValue!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a parameter to all queries in the collection.
|
/// Adds a parameter to all queries in the collection.
|
||||||
@@ -411,27 +311,8 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// Gets all unique parameters from all queries in the collection as a combined dictionary.
|
/// Gets all unique parameters from all queries in the collection as a combined dictionary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A dictionary containing all unique parameters across all queries.</returns>
|
/// <returns>A dictionary containing all unique parameters across all queries.</returns>
|
||||||
protected Dictionary<string, object> GetCombinedParameterDictionary()
|
protected Dictionary<string, object?> GetCombinedParameterDictionary()
|
||||||
{
|
=> QueryCollectionAnalysisHelper.GetCombinedParameters(_queryBreakdowns);
|
||||||
var combinedParams = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Add from ParameterList (parsed parameters)
|
|
||||||
foreach (var param in query.ParameterList)
|
|
||||||
{
|
|
||||||
combinedParams[param.Name] = param.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add/override from Parameters dictionary (manually added parameters)
|
|
||||||
foreach (var param in query.Parameters)
|
|
||||||
{
|
|
||||||
combinedParams[param.Key] = param.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinedParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all unique parameters from all queries in the collection.
|
/// Gets all unique parameters from all queries in the collection.
|
||||||
@@ -482,54 +363,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
|
|||||||
/// <returns>Parameter usage information.</returns>
|
/// <returns>Parameter usage information.</returns>
|
||||||
public IEnumerable<ParameterUsageReport> GetParameterUsageReport()
|
public IEnumerable<ParameterUsageReport> GetParameterUsageReport()
|
||||||
{
|
{
|
||||||
// Collect all unique parameter names from both ParameterList and Parameters dictionary
|
return QueryCollectionAnalysisHelper.GetParameterUsage(_queryBreakdowns)
|
||||||
var allParamNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
.Select(usage => new ParameterUsageReport
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Add from ParameterList (parsed parameters)
|
|
||||||
foreach (var param in query.ParameterList)
|
|
||||||
{
|
{
|
||||||
allParamNames.Add(param.Name);
|
ParameterName = usage.Name,
|
||||||
}
|
Value = usage.Value,
|
||||||
|
UsedInQueryCount = usage.UsedInQueryCount,
|
||||||
// Add from Parameters dictionary (manually added parameters)
|
TotalQueries = usage.TotalQueries
|
||||||
foreach (var paramName in query.Parameters.Keys)
|
});
|
||||||
{
|
|
||||||
allParamNames.Add(paramName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var paramName in allParamNames)
|
|
||||||
{
|
|
||||||
var queriesUsing = 0;
|
|
||||||
object? lastValue = null;
|
|
||||||
|
|
||||||
foreach (var query in _queryBreakdowns)
|
|
||||||
{
|
|
||||||
// Check ParameterList first (parsed)
|
|
||||||
var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
if (param != null)
|
|
||||||
{
|
|
||||||
queriesUsing++;
|
|
||||||
lastValue = param.Value;
|
|
||||||
}
|
|
||||||
// Also check Parameters dictionary (manually added)
|
|
||||||
else if (query.Parameters.TryGetValue(paramName, out var dictValue))
|
|
||||||
{
|
|
||||||
queriesUsing++;
|
|
||||||
lastValue = dictValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return new ParameterUsageReport
|
|
||||||
{
|
|
||||||
ParameterName = paramName,
|
|
||||||
Value = lastValue,
|
|
||||||
UsedInQueryCount = queriesUsing,
|
|
||||||
TotalQueries = _queryBreakdowns.Count
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,246 @@
|
|||||||
|
namespace Strata.SqlTools.Breakdowns.SqlServer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides dialect-agnostic analysis routines shared by the query breakdown collections.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// These operations depend only on members exposed by <see cref="QueryBreakdown"/>. Because the
|
||||||
|
/// PostgreSQL and Snowflake query types derive from <see cref="QueryBreakdown"/>, their collections
|
||||||
|
/// reuse this logic through <see cref="IEnumerable{T}"/> covariance instead of duplicating it.
|
||||||
|
/// </remarks>
|
||||||
|
public static class QueryCollectionAnalysisHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the total number of columns selected across all queries.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queries">The queries to inspect.</param>
|
||||||
|
/// <returns>Total column count.</returns>
|
||||||
|
public static int GetTotalSelectedColumns(IEnumerable<QueryBreakdown> queries)
|
||||||
|
{
|
||||||
|
return queries.Sum(q =>
|
||||||
|
!string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
|
||||||
|
? q.SelectClause.Clause.Split(',').Length
|
||||||
|
: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all unique table names referenced across all queries.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This provides a quick overview of which tables are being queried.
|
||||||
|
/// Note: This is a best-effort extraction and may not capture all table references,
|
||||||
|
/// especially in complex subqueries or with aliasing.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="queries">The queries to inspect.</param>
|
||||||
|
/// <returns>List of unique table names.</returns>
|
||||||
|
public static IEnumerable<string> GetUniqueTableReferences(IEnumerable<QueryBreakdown> queries)
|
||||||
|
{
|
||||||
|
var tables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var tableNames = queries
|
||||||
|
.Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
|
||||||
|
.SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
|
||||||
|
|
||||||
|
foreach (var table in tableNames)
|
||||||
|
{
|
||||||
|
tables.Add(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a summary of all queries including their types and basic composition.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queries">The queries to summarize.</param>
|
||||||
|
/// <returns>Summary information for each query.</returns>
|
||||||
|
public static IEnumerable<QuerySummary> GetQuerySummaries(IEnumerable<QueryBreakdown> queries)
|
||||||
|
{
|
||||||
|
return queries.Select((q, index) => new QuerySummary
|
||||||
|
{
|
||||||
|
Index = index,
|
||||||
|
HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
|
||||||
|
HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
|
||||||
|
HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
|
||||||
|
HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
|
||||||
|
HasHavingClause = !string.IsNullOrWhiteSpace(q.HavingClause?.Clause),
|
||||||
|
HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
|
||||||
|
HasJoins = false,
|
||||||
|
HasCTE = q.WithClauses.Count > 0,
|
||||||
|
ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
|
||||||
|
ParameterCount = q.ParameterList.Count(),
|
||||||
|
JoinCount = 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronizes parameter values across all queries in the collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Ensures all queries share the same parameter value based on parameter name. Later parameter
|
||||||
|
/// values override earlier ones if there are conflicts. Only parameters a query already defines
|
||||||
|
/// are synchronized, to avoid adding unused parameters.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="queries">The queries whose parameters should be synchronized.</param>
|
||||||
|
public static void SynchronizeParameters(IEnumerable<QueryBreakdown> queries)
|
||||||
|
{
|
||||||
|
var queryList = queries as IReadOnlyList<QueryBreakdown> ?? queries.ToList();
|
||||||
|
|
||||||
|
// Get all unique parameter names across all queries
|
||||||
|
var allParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var query in queryList)
|
||||||
|
{
|
||||||
|
foreach (var paramName in query.Parameters.Keys)
|
||||||
|
{
|
||||||
|
allParameterNames.Add(paramName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each parameter, use the last query's value and sync to all queries that have it
|
||||||
|
foreach (var paramName in allParameterNames)
|
||||||
|
{
|
||||||
|
object? lastValue = null;
|
||||||
|
bool parameterFound = false;
|
||||||
|
|
||||||
|
// Find the last query that has this parameter and get its value
|
||||||
|
for (int i = queryList.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (queryList[i].Parameters.ContainsKey(paramName))
|
||||||
|
{
|
||||||
|
lastValue = queryList[i].Parameters[paramName];
|
||||||
|
parameterFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronize the parameter value to all queries that have it
|
||||||
|
if (parameterFound)
|
||||||
|
{
|
||||||
|
foreach (var query in queryList.Where(q => q.Parameters.ContainsKey(paramName)))
|
||||||
|
{
|
||||||
|
query.Parameters[paramName] = lastValue!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all unique parameters from all queries in the collection as a combined dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Parameters parsed into <c>ParameterList</c> are added first; values from the manually managed
|
||||||
|
/// <c>Parameters</c> dictionary override them when names collide.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="queries">The queries to inspect.</param>
|
||||||
|
/// <returns>A dictionary containing all unique parameters across all queries.</returns>
|
||||||
|
public static Dictionary<string, object?> GetCombinedParameters(IEnumerable<QueryBreakdown> queries)
|
||||||
|
{
|
||||||
|
var combinedParams = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var query in queries)
|
||||||
|
{
|
||||||
|
// Add from ParameterList (parsed parameters)
|
||||||
|
foreach (var param in query.ParameterList)
|
||||||
|
{
|
||||||
|
combinedParams[param.Name] = param.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add/override from Parameters dictionary (manually added parameters)
|
||||||
|
foreach (var param in query.Parameters)
|
||||||
|
{
|
||||||
|
combinedParams[param.Key] = param.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes parameter usage statistics across all queries in the collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Callers project each tuple into their dialect-specific parameter usage report type so that
|
||||||
|
/// dialect-specific formatting (for example, <c>@</c> versus <c>:</c> parameter prefixes) is preserved.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="queries">The queries to inspect.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// One entry per unique parameter, carrying the parameter name, its last-seen value, the number of
|
||||||
|
/// queries using it, and the total query count.
|
||||||
|
/// </returns>
|
||||||
|
public static IEnumerable<(string Name, object? Value, int UsedInQueryCount, int TotalQueries)> GetParameterUsage(
|
||||||
|
IEnumerable<QueryBreakdown> queries)
|
||||||
|
{
|
||||||
|
var queryList = queries as IReadOnlyList<QueryBreakdown> ?? queries.ToList();
|
||||||
|
|
||||||
|
// Collect all unique parameter names from both ParameterList and Parameters dictionary
|
||||||
|
var allParamNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var query in queryList)
|
||||||
|
{
|
||||||
|
foreach (var param in query.ParameterList)
|
||||||
|
{
|
||||||
|
allParamNames.Add(param.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var paramName in query.Parameters.Keys)
|
||||||
|
{
|
||||||
|
allParamNames.Add(paramName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var paramName in allParamNames)
|
||||||
|
{
|
||||||
|
var queriesUsing = 0;
|
||||||
|
object? lastValue = null;
|
||||||
|
|
||||||
|
foreach (var query in queryList)
|
||||||
|
{
|
||||||
|
// Check ParameterList first (parsed)
|
||||||
|
var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (param != null)
|
||||||
|
{
|
||||||
|
queriesUsing++;
|
||||||
|
lastValue = param.Value;
|
||||||
|
}
|
||||||
|
// Also check Parameters dictionary (manually added)
|
||||||
|
else if (query.Parameters.TryGetValue(paramName, out var dictValue))
|
||||||
|
{
|
||||||
|
queriesUsing++;
|
||||||
|
lastValue = dictValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return (paramName, lastValue, queriesUsing, queryList.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts table names from a FROM clause on a best-effort basis.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fromClause">The FROM clause text.</param>
|
||||||
|
/// <returns>The table names referenced by the clause.</returns>
|
||||||
|
private static IEnumerable<string> ExtractTableNames(string fromClause)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(fromClause))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple extraction: split by comma and clean up aliases
|
||||||
|
var parts = fromClause.Split(',');
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
var trimmed = part.Trim();
|
||||||
|
|
||||||
|
// Remove alias (assuming format: table AS alias or table alias)
|
||||||
|
var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (tokens.Length > 0)
|
||||||
|
{
|
||||||
|
var tableName = tokens[0].Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(tableName))
|
||||||
|
{
|
||||||
|
yield return tableName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user