217 lines
8.0 KiB
C#
217 lines
8.0 KiB
C#
using Strata.SqlTools.SqlBreakdown.Classes;
|
|
using Strata.SqlTools.SqlBreakdown.Interfaces;
|
|
|
|
namespace Strata.SqlTools.Breakdowns.SqlServer;
|
|
|
|
/// <summary>
|
|
/// Base collection that manages a strongly typed list of query breakdowns and provides the
|
|
/// dialect-agnostic plumbing, filters, and analysis shared by the per-dialect collections.
|
|
/// </summary>
|
|
/// <typeparam name="TQuery">The concrete query breakdown type held by the collection.</typeparam>
|
|
/// <remarks>
|
|
/// Each dialect collection closes the generic over its own query type (for example,
|
|
/// <c>QueryBreakdownCollectionBase<Snowflake.QueryBreakdown></c>) so that public members such as
|
|
/// <see cref="QueryBreakdowns"/> and the <c>Where*</c> filters keep their dialect-specific element type.
|
|
/// </remarks>
|
|
public abstract class QueryBreakdownCollectionBase<TQuery> : SqlBreakdownCollection
|
|
where TQuery : QueryBreakdown
|
|
{
|
|
/// <summary>
|
|
/// Gets the backing list of typed query breakdowns.
|
|
/// </summary>
|
|
protected List<TQuery> QueryBreakdownList { get; }
|
|
|
|
/// <summary>
|
|
/// Initializes a new, empty instance.
|
|
/// </summary>
|
|
protected QueryBreakdownCollectionBase() : base()
|
|
{
|
|
QueryBreakdownList = new List<TQuery>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance with initial query breakdowns.
|
|
/// </summary>
|
|
/// <param name="queryBreakdowns">The initial collection of query breakdowns.</param>
|
|
protected QueryBreakdownCollectionBase(IEnumerable<TQuery> queryBreakdowns)
|
|
: base(queryBreakdowns?.Cast<ISqlBreakdown>() ?? Enumerable.Empty<ISqlBreakdown>())
|
|
{
|
|
QueryBreakdownList = new List<TQuery>(queryBreakdowns ?? Enumerable.Empty<TQuery>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the collection of query breakdowns.
|
|
/// </summary>
|
|
public IReadOnlyList<TQuery> QueryBreakdowns => QueryBreakdownList.AsReadOnly();
|
|
|
|
/// <summary>
|
|
/// Adds a query breakdown to the collection.
|
|
/// </summary>
|
|
/// <param name="queryBreakdown">The query breakdown to add.</param>
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="queryBreakdown"/> is null.</exception>
|
|
public void Add(TQuery queryBreakdown)
|
|
{
|
|
if (queryBreakdown == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(queryBreakdown));
|
|
}
|
|
|
|
QueryBreakdownList.Add(queryBreakdown);
|
|
base.Add(queryBreakdown);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds multiple query breakdowns to the collection.
|
|
/// </summary>
|
|
/// <param name="queryBreakdowns">The query breakdowns to add.</param>
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="queryBreakdowns"/> is null.</exception>
|
|
public void AddRange(IEnumerable<TQuery> queryBreakdowns)
|
|
{
|
|
if (queryBreakdowns == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(queryBreakdowns));
|
|
}
|
|
|
|
foreach (var breakdown in queryBreakdowns)
|
|
{
|
|
Add(breakdown);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a query breakdown from the collection.
|
|
/// </summary>
|
|
/// <param name="queryBreakdown">The query breakdown to remove.</param>
|
|
/// <returns>True if removed; otherwise, false.</returns>
|
|
public bool Remove(TQuery queryBreakdown)
|
|
{
|
|
var removed = QueryBreakdownList.Remove(queryBreakdown);
|
|
if (removed)
|
|
{
|
|
base.Remove(queryBreakdown);
|
|
}
|
|
return removed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all query breakdowns from the collection.
|
|
/// </summary>
|
|
public new void Clear()
|
|
{
|
|
QueryBreakdownList.Clear();
|
|
base.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters query breakdowns where the SELECT clause contains specific text.
|
|
/// </summary>
|
|
/// <param name="selectContains">The text to find in the SELECT clause.</param>
|
|
/// <returns>Filtered query breakdowns.</returns>
|
|
public IEnumerable<TQuery> WhereSelectContains(string selectContains)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(selectContains))
|
|
{
|
|
throw new ArgumentNullException(nameof(selectContains));
|
|
}
|
|
|
|
return QueryBreakdownList.Where(q =>
|
|
q.SelectClause?.Clause?.Contains(selectContains, StringComparison.OrdinalIgnoreCase) ?? false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters query breakdowns where the FROM clause contains specific text.
|
|
/// </summary>
|
|
/// <param name="tableNameContains">The table name or pattern to find.</param>
|
|
/// <returns>Filtered query breakdowns.</returns>
|
|
public IEnumerable<TQuery> WhereTableContains(string tableNameContains)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tableNameContains))
|
|
{
|
|
throw new ArgumentNullException(nameof(tableNameContains));
|
|
}
|
|
|
|
return QueryBreakdownList.Where(q =>
|
|
q.FromClause?.Clause?.Contains(tableNameContains, StringComparison.OrdinalIgnoreCase) ?? false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters query breakdowns that have a WHERE clause.
|
|
/// </summary>
|
|
/// <returns>Query breakdowns with WHERE clauses.</returns>
|
|
public IEnumerable<TQuery> WhereHaveWhereClause()
|
|
{
|
|
return QueryBreakdownList.Where(q =>
|
|
!string.IsNullOrWhiteSpace(q.WhereClause?.Clause));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters query breakdowns that do NOT have a WHERE clause.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is useful for identifying potentially risky queries that affect all rows.
|
|
/// </remarks>
|
|
/// <returns>Query breakdowns without WHERE clauses.</returns>
|
|
public IEnumerable<TQuery> WhereHaveNoWhereClause()
|
|
{
|
|
return QueryBreakdownList.Where(q =>
|
|
string.IsNullOrWhiteSpace(q.WhereClause?.Clause));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters query breakdowns that have a GROUP BY clause.
|
|
/// </summary>
|
|
/// <returns>Query breakdowns with GROUP BY clauses.</returns>
|
|
public IEnumerable<TQuery> WhereHaveGroupByClause()
|
|
{
|
|
return QueryBreakdownList.Where(q =>
|
|
!string.IsNullOrWhiteSpace(q.GroupByClause?.Clause));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters query breakdowns that have an ORDER BY clause.
|
|
/// </summary>
|
|
/// <returns>Query breakdowns with ORDER BY clauses.</returns>
|
|
public IEnumerable<TQuery> WhereHaveOrderByClause()
|
|
{
|
|
return QueryBreakdownList.Where(q =>
|
|
!string.IsNullOrWhiteSpace(q.OrderByClause?.Clause));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the total number of columns selected across all queries.
|
|
/// </summary>
|
|
/// <returns>Total column count.</returns>
|
|
public int GetTotalSelectedColumns()
|
|
=> QueryCollectionAnalysisHelper.GetTotalSelectedColumns(QueryBreakdownList);
|
|
|
|
/// <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>
|
|
/// <returns>List of unique table names.</returns>
|
|
public IEnumerable<string> GetUniqueTableReferences()
|
|
=> QueryCollectionAnalysisHelper.GetUniqueTableReferences(QueryBreakdownList);
|
|
|
|
/// <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>
|
|
public void SynchronizeParameters()
|
|
=> QueryCollectionAnalysisHelper.SynchronizeParameters(QueryBreakdownList);
|
|
|
|
/// <summary>
|
|
/// Gets all unique parameters from all queries in the collection as a combined dictionary.
|
|
/// </summary>
|
|
/// <returns>A dictionary containing all unique parameters across all queries.</returns>
|
|
protected Dictionary<string, object?> GetCombinedParameterDictionary()
|
|
=> QueryCollectionAnalysisHelper.GetCombinedParameters(QueryBreakdownList);
|
|
}
|