From f4d318b35c55866acfff243a6e1db42751b6cc40 Mon Sep 17 00:00:00 2001 From: Thom Lamb Date: Wed, 27 May 2026 17:47:12 -0500 Subject: [PATCH] refactor(dedup): IQueryBreakdownCollectionView eliminates Markdown adapter classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the per-dialect Adapter nested classes (which were themselves the leftover duplication after PR #22's first cut) with a shared IQueryBreakdownCollectionView interface implemented directly on each dialect's QueryBreakdownCollection. New foundation types in Strata.SqlTools.Breakdowns.SqlServer: - **IQueryBreakdownCollectionView** — dialect-neutral view exposing QueryCount, UniqueParameterCount, TotalSelectedColumns, UniqueTableCount, QueriesForReport (typed against the SqlServer QueryBreakdown base — PG/Snowflake satisfy via IReadOnlyList covariance), and ParameterUsageRecords. - **ParameterUsageRecord** — record type for per-parameter usage stats, projected from each dialect's ParameterUsageReport. The three dialect QueryBreakdownCollection classes now implement the interface explicitly — a handful of one-line forwarders per class. The Markdown layer's old ICollectionMarkdownData interface and the writer-internal ParameterUsageRow type are deleted; the template and writer take IQueryBreakdownCollectionView and ParameterUsageRecord directly. Net effect on the Markdown wrappers: - Markdown.SqlServer.QueryBreakdownCollectionGenerator loses its Adapter nested class and its 6 forwarders pass collection straight through. - Markdown.PostgreSql.QueryBreakdownCollectionGenerator ditto. - Markdown.Snowflake.QueryBreakdownCollectionGenerator migrated to the same pattern; its dialect-specific GenerateSnowflakeFeaturesAnalysis and feature-aware QueryCompositionReport callback stay intact (they still call CollectionReportWriter directly). Public API additions: IQueryBreakdownCollectionView and ParameterUsageRecord (both new, both opt-in). Public API removals: none — the dialect ParameterUsageReport classes are untouched and the dialect QueryBreakdownCollectionGenerator public surface is identical. All 1180 tests stay green. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Common/CollectionMarkdownGenerator.cs | 44 ++++++++-------- .../Common/CollectionReportWriter.cs | 24 +-------- .../Common/ICollectionMarkdownData.cs | 30 ----------- .../QueryBreakdownCollectionGenerator.cs | 34 +++---------- .../QueryBreakdownCollectionGenerator.cs | 45 ++-------------- .../QueryBreakdownCollectionGenerator.cs | 34 +++---------- .../Breakdowns/QueryBreakdownCollection.cs | 13 ++++- .../Breakdowns/QueryBreakdownCollection.cs | 13 ++++- .../IQueryBreakdownCollectionView.cs | 51 +++++++++++++++++++ .../Breakdowns/QueryBreakdownCollection.cs | 13 ++++- 10 files changed, 128 insertions(+), 173 deletions(-) delete mode 100644 src/Strata.SqlTools.Markdown/Common/ICollectionMarkdownData.cs create mode 100644 src/Strata.SqlTools.SqlServer/Breakdowns/IQueryBreakdownCollectionView.cs diff --git a/src/Strata.SqlTools.Markdown/Common/CollectionMarkdownGenerator.cs b/src/Strata.SqlTools.Markdown/Common/CollectionMarkdownGenerator.cs index 977042b..7df8a0e 100644 --- a/src/Strata.SqlTools.Markdown/Common/CollectionMarkdownGenerator.cs +++ b/src/Strata.SqlTools.Markdown/Common/CollectionMarkdownGenerator.cs @@ -1,37 +1,39 @@ +using Strata.SqlTools.Breakdowns.SqlServer; + namespace Strata.SqlTools.Markdown.Common; /// -/// Shared method bodies for the per-dialect QueryBreakdownCollectionGenerator -/// classes. Each dialect wraps its QueryBreakdownCollection in an -/// adapter and supplies a dialect-specific -/// ; this template handles the rest. +/// Shared method bodies for the per-dialect QueryBreakdownCollectionGenerator classes. +/// Operates against the dialect-neutral contract +/// that every dialect's QueryBreakdownCollection implements, so the dialect wrappers +/// can pass their collection straight through with no per-dialect adapter required. /// internal static class CollectionMarkdownGenerator { - public static string GenerateCollectionReport(ICollectionMarkdownData data, MarkdownDialectFormat format, string? title) + public static string GenerateCollectionReport(IQueryBreakdownCollectionView view, MarkdownDialectFormat format, string? title) => CollectionReportWriter.CollectionReport(title, new[] { - GenerateCollectionSummary(data), - GenerateParameterAnalysis(data, format), - GenerateQueryCompositionReport(data, format) + GenerateCollectionSummary(view), + GenerateParameterAnalysis(view, format), + GenerateQueryCompositionReport(view, format) }); - public static string GenerateCollectionSummary(ICollectionMarkdownData data) + public static string GenerateCollectionSummary(IQueryBreakdownCollectionView view) => CollectionReportWriter.CollectionSummary( - data.QueryCount, - data.UniqueParameterCount, - data.TotalSelectedColumns, - data.UniqueTableCount); + view.QueryCount, + view.UniqueParameterCount, + view.TotalSelectedColumns, + view.UniqueTableCount); - public static string GenerateParameterAnalysis(ICollectionMarkdownData data, MarkdownDialectFormat format) - => CollectionReportWriter.ParameterAnalysis(data.ParameterRows.ToList(), data.QueriesForReport, format); + public static string GenerateParameterAnalysis(IQueryBreakdownCollectionView view, MarkdownDialectFormat format) + => CollectionReportWriter.ParameterAnalysis(view.ParameterUsageRecords.ToList(), view.QueriesForReport, format); - public static string GenerateParameterDependencyDiagram(ICollectionMarkdownData data, MarkdownDialectFormat format) - => CollectionReportWriter.ParameterDependencyDiagram(data.QueriesForReport, format); + public static string GenerateParameterDependencyDiagram(IQueryBreakdownCollectionView view, MarkdownDialectFormat format) + => CollectionReportWriter.ParameterDependencyDiagram(view.QueriesForReport, format); - public static string GenerateQueryCompositionReport(ICollectionMarkdownData data, MarkdownDialectFormat format) - => CollectionReportWriter.QueryCompositionReport(data.QueriesForReport, format); + public static string GenerateQueryCompositionReport(IQueryBreakdownCollectionView view, MarkdownDialectFormat format) + => CollectionReportWriter.QueryCompositionReport(view.QueriesForReport, format); - public static string GenerateBatchFlowDiagram(ICollectionMarkdownData data, string? openLabel, string? closeLabel) - => CollectionReportWriter.BatchFlowDiagram(data.QueryCount, openLabel, closeLabel); + public static string GenerateBatchFlowDiagram(IQueryBreakdownCollectionView view, string? openLabel, string? closeLabel) + => CollectionReportWriter.BatchFlowDiagram(view.QueryCount, openLabel, closeLabel); } diff --git a/src/Strata.SqlTools.Markdown/Common/CollectionReportWriter.cs b/src/Strata.SqlTools.Markdown/Common/CollectionReportWriter.cs index d869f7e..bfadcd5 100644 --- a/src/Strata.SqlTools.Markdown/Common/CollectionReportWriter.cs +++ b/src/Strata.SqlTools.Markdown/Common/CollectionReportWriter.cs @@ -32,28 +32,6 @@ internal sealed class MarkdownDialectFormat public required bool IncludeHavingRow { get; init; } } -/// -/// Carries the precomputed usage statistics for a single parameter, decoupling the writer from each -/// dialect's concrete parameter-usage report type. -/// -internal sealed class ParameterUsageRow -{ - /// The parameter name (without any dialect prefix). - public required string ParameterName { get; init; } - - /// Whether the parameter is used by every query in the collection. - public required bool IsUsedInAllQueries { get; init; } - - /// The number of queries that use the parameter. - public required int UsedInQueryCount { get; init; } - - /// The total number of queries in the collection. - public required int TotalQueries { get; init; } - - /// The parameter's last-seen value. - public required object? Value { get; init; } -} - /// /// Renders the dialect-agnostic Markdown for query breakdown collection reports. The per-dialect /// generators supply a and precomputed data; this writer produces @@ -113,7 +91,7 @@ internal static class CollectionReportWriter /// Renders the parameter analysis section, including the dependency diagram. /// public static string ParameterAnalysis( - IReadOnlyList parameters, + IReadOnlyList parameters, IEnumerable queries, MarkdownDialectFormat format) { diff --git a/src/Strata.SqlTools.Markdown/Common/ICollectionMarkdownData.cs b/src/Strata.SqlTools.Markdown/Common/ICollectionMarkdownData.cs deleted file mode 100644 index 0dd70d1..0000000 --- a/src/Strata.SqlTools.Markdown/Common/ICollectionMarkdownData.cs +++ /dev/null @@ -1,30 +0,0 @@ -using SqlServerBreakdowns = Strata.SqlTools.Breakdowns.SqlServer; - -namespace Strata.SqlTools.Markdown.Common; - -/// -/// Dialect-neutral view of a QueryBreakdownCollection as seen by the Markdown -/// generators. Each per-dialect QueryBreakdownCollectionGenerator wraps its -/// dialect-specific collection in an implementation of this interface so the shared -/// template can operate uniformly. -/// -internal interface ICollectionMarkdownData -{ - int QueryCount { get; } - int UniqueParameterCount { get; } - int TotalSelectedColumns { get; } - int UniqueTableCount { get; } - - /// - /// Queries as the base type. Used - /// directly by - /// (which needs ) and via covariance by the other - /// writer methods that accept . - /// - IReadOnlyList QueriesForReport { get; } - - /// - /// Parameter usage already mapped into the writer's dialect-agnostic row type. - /// - IEnumerable ParameterRows { get; } -} diff --git a/src/Strata.SqlTools.Markdown/PostgreSql/QueryBreakdownCollectionGenerator.cs b/src/Strata.SqlTools.Markdown/PostgreSql/QueryBreakdownCollectionGenerator.cs index 1a28895..64dca0a 100644 --- a/src/Strata.SqlTools.Markdown/PostgreSql/QueryBreakdownCollectionGenerator.cs +++ b/src/Strata.SqlTools.Markdown/PostgreSql/QueryBreakdownCollectionGenerator.cs @@ -24,63 +24,41 @@ public static class QueryBreakdownCollectionGenerator /// Generates a comprehensive collection report in Markdown format with PostgreSQL-specific information. /// public static string GenerateCollectionReport(QueryBreakdownCollection collection, string? title = null) - => CollectionMarkdownGenerator.GenerateCollectionReport(Adapt(collection), Format, title); + => CollectionMarkdownGenerator.GenerateCollectionReport(collection, Format, title); /// /// Generates a summary section for the collection. /// public static string GenerateCollectionSummary(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateCollectionSummary(Adapt(collection)); + => CollectionMarkdownGenerator.GenerateCollectionSummary(collection); /// /// Generates a parameter analysis report with PostgreSQL parameter syntax support. /// public static string GenerateParameterAnalysis(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateParameterAnalysis(Adapt(collection), Format); + => CollectionMarkdownGenerator.GenerateParameterAnalysis(collection, Format); /// /// Generates a Mermaid diagram showing parameter dependencies across queries. /// public static string GenerateParameterDependencyDiagram(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateParameterDependencyDiagram(Adapt(collection), Format); + => CollectionMarkdownGenerator.GenerateParameterDependencyDiagram(collection, Format); /// /// Generates a detailed query composition report. /// public static string GenerateQueryCompositionReport(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateQueryCompositionReport(Adapt(collection), Format); + => CollectionMarkdownGenerator.GenerateQueryCompositionReport(collection, Format); /// /// Generates a batch execution flow diagram for PostgreSQL. /// public static string GenerateBatchFlowDiagram(QueryBreakdownCollection collection, bool includeTransaction = false) => CollectionMarkdownGenerator.GenerateBatchFlowDiagram( - Adapt(collection), + collection, includeTransaction ? "BEGIN" : null, includeTransaction ? "COMMIT" : null); - private static ICollectionMarkdownData Adapt(QueryBreakdownCollection collection) => new Adapter(collection); - - private sealed class Adapter : ICollectionMarkdownData - { - private readonly QueryBreakdownCollection _c; - public Adapter(QueryBreakdownCollection c) { _c = c; } - public int QueryCount => _c.QueryBreakdowns.Count; - public int UniqueParameterCount => _c.GetAllUniqueParameters().Count(); - public int TotalSelectedColumns => _c.GetTotalSelectedColumns(); - public int UniqueTableCount => _c.GetUniqueTableReferences().Count(); - public IReadOnlyList QueriesForReport => _c.QueryBreakdowns; - public IEnumerable ParameterRows - => _c.GetParameterUsageReport().Select(p => new ParameterUsageRow - { - ParameterName = p.ParameterName, - IsUsedInAllQueries = p.IsUsedInAllQueries, - UsedInQueryCount = p.UsedInQueryCount, - TotalQueries = p.TotalQueries, - Value = p.Value - }); - } - /// /// Formats a parameter using PostgreSQL syntax: $n for positional, :name for named. /// diff --git a/src/Strata.SqlTools.Markdown/Snowflake/QueryBreakdownCollectionGenerator.cs b/src/Strata.SqlTools.Markdown/Snowflake/QueryBreakdownCollectionGenerator.cs index 42b09dd..113f0d9 100644 --- a/src/Strata.SqlTools.Markdown/Snowflake/QueryBreakdownCollectionGenerator.cs +++ b/src/Strata.SqlTools.Markdown/Snowflake/QueryBreakdownCollectionGenerator.cs @@ -24,9 +24,6 @@ public static class QueryBreakdownCollectionGenerator /// /// Generates a comprehensive collection report in Markdown format with Snowflake-specific information. /// - /// The QueryBreakdownCollection to document. - /// Optional title for the report. - /// A string containing the Markdown documentation. public static string GenerateCollectionReport(QueryBreakdownCollection collection, string? title = null) => CollectionReportWriter.CollectionReport(title, new[] { @@ -39,20 +36,12 @@ public static class QueryBreakdownCollectionGenerator /// /// Generates a summary section for the collection. /// - /// The QueryBreakdownCollection to summarize. - /// Markdown summary section. public static string GenerateCollectionSummary(QueryBreakdownCollection collection) - => CollectionReportWriter.CollectionSummary( - collection.QueryBreakdowns.Count, - collection.GetAllUniqueParameters().Count(), - collection.GetTotalSelectedColumns(), - collection.GetUniqueTableReferences().Count()); + => CollectionMarkdownGenerator.GenerateCollectionSummary(collection); /// /// Generates a Snowflake-specific features analysis section. /// - /// The QueryBreakdownCollection to analyze. - /// Markdown Snowflake features section. public static string GenerateSnowflakeFeaturesAnalysis(QueryBreakdownCollection collection) { var sb = new StringBuilder(); @@ -73,24 +62,18 @@ public static class QueryBreakdownCollectionGenerator /// /// Generates a parameter analysis report with Snowflake parameter syntax support. /// - /// The QueryBreakdownCollection to analyze. - /// Markdown parameter analysis section. public static string GenerateParameterAnalysis(QueryBreakdownCollection collection) - => CollectionReportWriter.ParameterAnalysis(MapParameters(collection), collection.QueryBreakdowns, Format); + => CollectionMarkdownGenerator.GenerateParameterAnalysis(collection, Format); /// /// Generates a Mermaid diagram showing parameter dependencies across queries. /// - /// The QueryBreakdownCollection to visualize. - /// Mermaid diagram markdown. public static string GenerateParameterDependencyDiagram(QueryBreakdownCollection collection) - => CollectionReportWriter.ParameterDependencyDiagram(collection.QueryBreakdowns, Format); + => CollectionMarkdownGenerator.GenerateParameterDependencyDiagram(collection, Format); /// /// Generates a detailed query composition report with Snowflake-specific information. /// - /// The QueryBreakdownCollection to report on. - /// Markdown composition report section. public static string GenerateQueryCompositionReport(QueryBreakdownCollection collection) { var stageQueries = collection.WhereUseStageReference().ToList(); @@ -105,30 +88,12 @@ public static class QueryBreakdownCollectionGenerator /// /// Generates a batch execution flow diagram for Snowflake. /// - /// The QueryBreakdownCollection to visualize. - /// Whether to show session setup statements. - /// Mermaid diagram markdown. public static string GenerateBatchFlowDiagram(QueryBreakdownCollection collection, bool includeSessionSetup = false) - => CollectionReportWriter.BatchFlowDiagram( - collection.QueryBreakdowns.Count, + => CollectionMarkdownGenerator.GenerateBatchFlowDiagram( + collection, includeSessionSetup ? "Session Setup" : null, closeLabel: null); - /// - /// Maps the collection's parameter usage report into the writer's dialect-agnostic rows. - /// - private static List MapParameters(QueryBreakdownCollection collection) - => collection.GetParameterUsageReport() - .Select(p => new ParameterUsageRow - { - ParameterName = p.ParameterName, - IsUsedInAllQueries = p.IsUsedInAllQueries, - UsedInQueryCount = p.UsedInQueryCount, - TotalQueries = p.TotalQueries, - Value = p.Value - }) - .ToList(); - /// /// Appends Snowflake-specific feature information for a query. /// diff --git a/src/Strata.SqlTools.Markdown/SqlServer/QueryBreakdownCollectionGenerator.cs b/src/Strata.SqlTools.Markdown/SqlServer/QueryBreakdownCollectionGenerator.cs index 07ba541..1a7db2f 100644 --- a/src/Strata.SqlTools.Markdown/SqlServer/QueryBreakdownCollectionGenerator.cs +++ b/src/Strata.SqlTools.Markdown/SqlServer/QueryBreakdownCollectionGenerator.cs @@ -23,63 +23,41 @@ public static class QueryBreakdownCollectionGenerator /// Generates a comprehensive collection report in Markdown format. /// public static string GenerateCollectionReport(QueryBreakdownCollection collection, string? title = null) - => CollectionMarkdownGenerator.GenerateCollectionReport(Adapt(collection), Format, title); + => CollectionMarkdownGenerator.GenerateCollectionReport(collection, Format, title); /// /// Generates a summary section for the collection. /// public static string GenerateCollectionSummary(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateCollectionSummary(Adapt(collection)); + => CollectionMarkdownGenerator.GenerateCollectionSummary(collection); /// /// Generates a parameter analysis report. /// public static string GenerateParameterAnalysis(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateParameterAnalysis(Adapt(collection), Format); + => CollectionMarkdownGenerator.GenerateParameterAnalysis(collection, Format); /// /// Generates a Mermaid diagram showing parameter dependencies across queries. /// public static string GenerateParameterDependencyDiagram(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateParameterDependencyDiagram(Adapt(collection), Format); + => CollectionMarkdownGenerator.GenerateParameterDependencyDiagram(collection, Format); /// /// Generates a detailed query composition report. /// public static string GenerateQueryCompositionReport(QueryBreakdownCollection collection) - => CollectionMarkdownGenerator.GenerateQueryCompositionReport(Adapt(collection), Format); + => CollectionMarkdownGenerator.GenerateQueryCompositionReport(collection, Format); /// /// Generates a batch execution flow diagram. /// public static string GenerateBatchFlowDiagram(QueryBreakdownCollection collection, bool includeTransaction = false) => CollectionMarkdownGenerator.GenerateBatchFlowDiagram( - Adapt(collection), + collection, includeTransaction ? "BEGIN TRANSACTION" : null, includeTransaction ? "COMMIT TRANSACTION" : null); - private static ICollectionMarkdownData Adapt(QueryBreakdownCollection collection) => new Adapter(collection); - - private sealed class Adapter : ICollectionMarkdownData - { - private readonly QueryBreakdownCollection _c; - public Adapter(QueryBreakdownCollection c) { _c = c; } - public int QueryCount => _c.QueryBreakdowns.Count; - public int UniqueParameterCount => _c.GetAllUniqueParameters().Count(); - public int TotalSelectedColumns => _c.GetTotalSelectedColumns(); - public int UniqueTableCount => _c.GetUniqueTableReferences().Count(); - public IReadOnlyList QueriesForReport => _c.QueryBreakdowns; - public IEnumerable ParameterRows - => _c.GetParameterUsageReport().Select(p => new ParameterUsageRow - { - ParameterName = p.ParameterName, - IsUsedInAllQueries = p.IsUsedInAllQueries, - UsedInQueryCount = p.UsedInQueryCount, - TotalQueries = p.TotalQueries, - Value = p.Value - }); - } - /// /// Gets the parameter type name from a parameter value. /// diff --git a/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs b/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs index b2e6186..7c8b7f1 100644 --- a/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs +++ b/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs @@ -12,7 +12,7 @@ namespace Strata.SqlTools.Breakdowns.PostgreSql; /// including support for PostgreSQL features like schema-qualified identifiers, /// LIMIT/OFFSET clauses, parameterized queries using $1, $2 syntax, and CTEs. /// -public class QueryBreakdownCollection : SqlBreakdownCollection +public class QueryBreakdownCollection : SqlBreakdownCollection, SqlServer.IQueryBreakdownCollectionView { private readonly List _queryBreakdowns; @@ -199,6 +199,17 @@ public class QueryBreakdownCollection : SqlBreakdownCollection TotalQueries = usage.TotalQueries }); } + + // --- IQueryBreakdownCollectionView --- + + int SqlServer.IQueryBreakdownCollectionView.QueryCount => QueryBreakdowns.Count; + int SqlServer.IQueryBreakdownCollectionView.UniqueParameterCount => GetAllUniqueParameters().Count(); + int SqlServer.IQueryBreakdownCollectionView.TotalSelectedColumns => GetTotalSelectedColumns(); + int SqlServer.IQueryBreakdownCollectionView.UniqueTableCount => GetUniqueTableReferences().Count(); + IReadOnlyList SqlServer.IQueryBreakdownCollectionView.QueriesForReport => QueryBreakdowns; + IEnumerable SqlServer.IQueryBreakdownCollectionView.ParameterUsageRecords + => GetParameterUsageReport().Select(p => new SqlServer.ParameterUsageRecord( + p.ParameterName, p.Value, p.UsedInQueryCount, p.TotalQueries, p.IsUsedInAllQueries)); } /// diff --git a/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs b/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs index 7875456..fd17924 100644 --- a/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs +++ b/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs @@ -13,7 +13,7 @@ namespace Strata.SqlTools.Breakdowns.Snowflake; /// time travel, snowflake-specific parameters (:parameter and @parameter syntax), /// and proper batch handling. /// -public class QueryBreakdownCollection : SqlServer.QueryBreakdownCollectionBase +public class QueryBreakdownCollection : SqlServer.QueryBreakdownCollectionBase, SqlServer.IQueryBreakdownCollectionView { /// /// Initializes a new instance of the class for Snowflake. @@ -406,6 +406,17 @@ public class QueryBreakdownCollection : SqlServer.QueryBreakdownCollectionBase QueryBreakdowns.Count; + int SqlServer.IQueryBreakdownCollectionView.UniqueParameterCount => GetAllUniqueParameters().Count(); + int SqlServer.IQueryBreakdownCollectionView.TotalSelectedColumns => GetTotalSelectedColumns(); + int SqlServer.IQueryBreakdownCollectionView.UniqueTableCount => GetUniqueTableReferences().Count(); + IReadOnlyList SqlServer.IQueryBreakdownCollectionView.QueriesForReport => QueryBreakdowns; + IEnumerable SqlServer.IQueryBreakdownCollectionView.ParameterUsageRecords + => GetParameterUsageReport().Select(p => new SqlServer.ParameterUsageRecord( + p.ParameterName, p.Value, p.UsedInQueryCount, p.TotalQueries, p.IsUsedInAllQueries)); + /// /// Gets a summary of all queries including their types and basic composition. /// diff --git a/src/Strata.SqlTools.SqlServer/Breakdowns/IQueryBreakdownCollectionView.cs b/src/Strata.SqlTools.SqlServer/Breakdowns/IQueryBreakdownCollectionView.cs new file mode 100644 index 0000000..cb135c8 --- /dev/null +++ b/src/Strata.SqlTools.SqlServer/Breakdowns/IQueryBreakdownCollectionView.cs @@ -0,0 +1,51 @@ +namespace Strata.SqlTools.Breakdowns.SqlServer; + +/// +/// Dialect-neutral view of a query breakdown collection. Exposes the metrics and the per-query +/// access surface that the Markdown report generators need — implemented by each dialect's +/// QueryBreakdownCollection so the Markdown layer can operate without dialect-specific +/// adapters. +/// +/// +/// is typed against the SqlServer +/// base; PostgreSQL and Snowflake collections satisfy this via +/// covariance because their dialect-specific QueryBreakdown classes derive from this one. +/// +public interface IQueryBreakdownCollectionView +{ + /// Number of queries in the collection. + int QueryCount { get; } + + /// Number of unique parameter names across all queries. + int UniqueParameterCount { get; } + + /// Number of columns selected across all queries. + int TotalSelectedColumns { get; } + + /// Number of distinct table references across all queries. + int UniqueTableCount { get; } + + /// The queries, as the shared base type. + IReadOnlyList QueriesForReport { get; } + + /// Per-parameter usage records, mapped to the dialect-neutral + /// type. + IEnumerable ParameterUsageRecords { get; } +} + +/// +/// Dialect-neutral, immutable record describing how often a parameter is used across the queries +/// in a collection. Each dialect's ParameterUsageReport projects into this type so the +/// Markdown layer can present usage tables without knowing about dialect-specific shapes. +/// +/// The parameter name (no dialect prefix). +/// The parameter's last-seen value. +/// How many queries reference this parameter. +/// Total queries in the collection. +/// Whether every query uses the parameter. +public sealed record ParameterUsageRecord( + string ParameterName, + object? Value, + int UsedInQueryCount, + int TotalQueries, + bool IsUsedInAllQueries); diff --git a/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs b/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs index f18d779..2a78b9c 100644 --- a/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs +++ b/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs @@ -9,7 +9,7 @@ namespace Strata.SqlTools.Breakdowns.SqlServer; /// This class extends SqlBreakdownCollection with SQL Server-specific functionality, /// including support for T-SQL features like batches (GO), temporary tables, stored procedures, and CTEs. /// -public class QueryBreakdownCollection : QueryBreakdownCollectionBase +public class QueryBreakdownCollection : QueryBreakdownCollectionBase, IQueryBreakdownCollectionView { /// /// Initializes a new instance of the class. @@ -187,6 +187,17 @@ public class QueryBreakdownCollection : QueryBreakdownCollectionBase QueryBreakdowns.Count; + int IQueryBreakdownCollectionView.UniqueParameterCount => GetAllUniqueParameters().Count(); + int IQueryBreakdownCollectionView.TotalSelectedColumns => GetTotalSelectedColumns(); + int IQueryBreakdownCollectionView.UniqueTableCount => GetUniqueTableReferences().Count(); + IReadOnlyList IQueryBreakdownCollectionView.QueriesForReport => QueryBreakdowns; + IEnumerable IQueryBreakdownCollectionView.ParameterUsageRecords + => GetParameterUsageReport().Select(p => new ParameterUsageRecord( + p.ParameterName, p.Value, p.UsedInQueryCount, p.TotalQueries, p.IsUsedInAllQueries)); + /// /// Helper method to get SQL Server data type from a .NET object. ///