refactor(dedup): extract AppendWithClauseSection for the WITH/CTE block
`SqlServer.QueryBreakdown.GetSqlBreakdown` and
`Snowflake.QueryBreakdown.GetSql` each carried a 24-line copy of the
same CTE-rendering loop ("WITH" keyword, optional RECURSIVE, per-clause
header, anchor/UNION ALL/recursive query, closing parens). The two
copies differed only by indent (5/10 spaces vs 4/8) and a trailing
space after the keyword.
Hoist the loop into `protected virtual void AppendWithClauseSection(
StringBuilder, string withClauseIndent, string queryBodyIndent)` on
`SqlServer.QueryBreakdown`. Each caller invokes it with its dialect's
preferred indents; Snowflake's mid-method copy is deleted entirely.
Standardizes on the no-trailing-space "WITH" form (Snowflake's) — was
"WITH " (trailing space) in the SqlServer original. Visible only as a
trailing space before the newline in non-recursive output, which no
tests assert on.
All 1180 tests stay green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
620b85a61d
commit
089d4f6000
@@ -451,62 +451,7 @@ public class QueryBreakdown : SqlServerQueryBreakdown
|
||||
}
|
||||
}
|
||||
|
||||
if (IsUsingWithClause)
|
||||
{
|
||||
// Check if any WITH clause is recursive
|
||||
bool hasRecursive = WithClauses.Any(wc => wc.IsRecursive);
|
||||
sb.Append("WITH");
|
||||
if (hasRecursive)
|
||||
{
|
||||
sb.Append(" RECURSIVE");
|
||||
}
|
||||
sb.AppendLine();
|
||||
|
||||
for (int i = 0; i < WithClauses.Count; i++)
|
||||
{
|
||||
var withClause = WithClauses[i];
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
sb.Append(',');
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
// Include comment if present
|
||||
if (!string.IsNullOrWhiteSpace(withClause.Comment))
|
||||
{
|
||||
sb.AppendLine($" {withClause.Comment}");
|
||||
}
|
||||
|
||||
// Write CTE name with optional column list
|
||||
var cteName = withClause.TableName;
|
||||
if (withClause.ColumnList != null && withClause.ColumnList.Count > 0)
|
||||
{
|
||||
var columnList = string.Join(", ", withClause.ColumnList);
|
||||
cteName = $"{withClause.TableName} ({columnList})";
|
||||
}
|
||||
|
||||
sb.AppendLine($" {cteName} AS (");
|
||||
|
||||
if (withClause.IsRecursive && withClause.RecursiveQuery != null)
|
||||
{
|
||||
// For recursive CTEs: anchor query UNION ALL recursive query
|
||||
var anchorSql = withClause.Query?.GetSql(includeSetupFinish: false).Trim() ?? string.Empty;
|
||||
var recursiveSql = withClause.RecursiveQuery.GetSql(includeSetupFinish: false).Trim() ?? string.Empty;
|
||||
sb.AppendLine($" {anchorSql}");
|
||||
sb.AppendLine(" UNION ALL");
|
||||
sb.AppendLine($" {recursiveSql}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-recursive CTEs: just the single query
|
||||
var withSql = withClause.Query?.GetSql(includeSetupFinish: false).Trim() ?? string.Empty;
|
||||
sb.AppendLine($" {withSql}");
|
||||
}
|
||||
sb.Append(" )");
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
AppendWithClauseSection(sb, withClauseIndent: " ", queryBodyIndent: " ");
|
||||
|
||||
// Snowflake SELECT syntax
|
||||
sb.Append(StatementParser.KeywordSelect);
|
||||
|
||||
@@ -547,74 +547,79 @@ public class QueryBreakdown : SqlBreakdownBase, IQueryBreakdown
|
||||
return mergedParams;
|
||||
}
|
||||
|
||||
#pragma warning disable S3776 // Cognitive Complexity of methods should not be too high
|
||||
/// <summary>
|
||||
/// Renders the <c>WITH [RECURSIVE]</c> CTE section into <paramref name="sb"/> if any
|
||||
/// <see cref="WithClauses"/> are present. Shared by dialects (SqlServer uses 5-space
|
||||
/// outer / 10-space inner indents; Snowflake passes 4-/8-space).
|
||||
/// </summary>
|
||||
/// <param name="sb">The builder accumulating the SQL.</param>
|
||||
/// <param name="withClauseIndent">Indent used for the CTE name line and the closing <c>)</c>.</param>
|
||||
/// <param name="queryBodyIndent">Indent used for each anchor/recursive query body line and the <c>UNION ALL</c>.</param>
|
||||
protected virtual void AppendWithClauseSection(StringBuilder sb, string withClauseIndent, string queryBodyIndent)
|
||||
{
|
||||
if (!IsUsingWithClause)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasRecursive = _withClauses.Any(wc => wc.IsRecursive);
|
||||
sb.Append("WITH");
|
||||
if (hasRecursive)
|
||||
{
|
||||
sb.Append(" RECURSIVE");
|
||||
}
|
||||
sb.AppendLine();
|
||||
|
||||
for (int i = 0; i < _withClauses.Count; i++)
|
||||
{
|
||||
var withClause = _withClauses[i];
|
||||
var anchorSql = withClause.Query?.GetSql(includeSetupFinish: false).Trim() ?? string.Empty;
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
sb.Append(',');
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(withClause.Comment))
|
||||
{
|
||||
sb.AppendLine($"{withClauseIndent}{withClause.Comment}");
|
||||
}
|
||||
|
||||
var cteName = withClause.TableName;
|
||||
if (withClause.ColumnList != null && withClause.ColumnList.Count > 0)
|
||||
{
|
||||
var columnList = string.Join(", ", withClause.ColumnList);
|
||||
cteName = $"{withClause.TableName} ({columnList})";
|
||||
}
|
||||
sb.AppendLine($"{withClauseIndent}{cteName} AS (");
|
||||
|
||||
if (withClause.IsRecursive && withClause.RecursiveQuery != null)
|
||||
{
|
||||
var recursiveSql = withClause.RecursiveQuery.GetSql(includeSetupFinish: false).Trim();
|
||||
sb.AppendLine($"{queryBodyIndent}{anchorSql}");
|
||||
sb.AppendLine($"{queryBodyIndent}UNION ALL");
|
||||
sb.AppendLine($"{queryBodyIndent}{recursiveSql}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{queryBodyIndent}{anchorSql}");
|
||||
}
|
||||
|
||||
sb.Append($"{withClauseIndent})");
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SQL breakdown as a string (query-specific implementation).
|
||||
/// </summary>
|
||||
/// <returns>The SELECT SQL statement.</returns>
|
||||
protected override string GetSqlBreakdown()
|
||||
#pragma warning restore S3776
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (IsUsingWithClause)
|
||||
{
|
||||
// Check if any CTE is recursive - if so, add RECURSIVE keyword
|
||||
bool hasRecursive = _withClauses.Any(wc => wc.IsRecursive);
|
||||
sb.Append("WITH ");
|
||||
if (hasRecursive)
|
||||
{
|
||||
sb.AppendLine("RECURSIVE");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
for (int i = 0; i < _withClauses.Count; i++)
|
||||
{
|
||||
var withClause = _withClauses[i];
|
||||
var anchorSql = withClause.Query?.GetSql(includeSetupFinish: false).Trim() ?? string.Empty;
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
sb.Append(',');
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
// Include comment if present
|
||||
if (!string.IsNullOrWhiteSpace(withClause.Comment))
|
||||
{
|
||||
sb.AppendLine($" {withClause.Comment}");
|
||||
}
|
||||
|
||||
// Write CTE name with optional column list
|
||||
var cteName = withClause.TableName;
|
||||
if (withClause.ColumnList != null && withClause.ColumnList.Count > 0)
|
||||
{
|
||||
var columnList = string.Join(", ", withClause.ColumnList);
|
||||
cteName = $"{withClause.TableName} ({columnList})";
|
||||
}
|
||||
sb.AppendLine($" {cteName} AS (");
|
||||
|
||||
if (withClause.IsRecursive && withClause.RecursiveQuery != null)
|
||||
{
|
||||
// For recursive CTEs: anchor query UNION ALL recursive query
|
||||
var recursiveSql = withClause.RecursiveQuery.GetSql(includeSetupFinish: false).Trim();
|
||||
sb.AppendLine($" {anchorSql}");
|
||||
sb.AppendLine(" UNION ALL");
|
||||
sb.AppendLine($" {recursiveSql}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-recursive CTEs: just the single query
|
||||
sb.AppendLine($" {anchorSql}");
|
||||
}
|
||||
|
||||
sb.Append(" )");
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
AppendWithClauseSection(sb, withClauseIndent: " ", queryBodyIndent: " ");
|
||||
|
||||
sb.AppendLine("SELECT ");
|
||||
if (!string.IsNullOrEmpty(SelectClause.Comment))
|
||||
|
||||
Reference in New Issue
Block a user