using Strata.SqlTools.SqlBreakdown.Interfaces.QueryEngine;
namespace Strata.SqlTools.SqlBreakdown.Classes;
///
/// Represents a WITH clause (Common Table Expression) with its structure and parsed query.
///
///
///
/// The Clause property inherited from SqlClause contains the full CTE definition text for round-trip parsing.
/// The TableName property identifies the CTE, while Sql contains the parsed query structure.
/// The Query property provides access to the full query breakdown including parameters.
///
///
/// Recursive CTE Limitations and Requirements:
///
/// - Must have IsRecursive = true
/// - Must provide a RecursiveQuery (the UNION ALL recursive member)
/// - The Query property represents the anchor member (non-recursive base case)
/// - Both anchor and recursive members must return the same number of columns with compatible types
/// - ColumnList is recommended but not required; helps ensure column consistency
/// - RecursiveQuery typically references the CTE's TableName in its FROM clause
/// - Always include a termination condition in the recursive query's WHERE clause to prevent infinite loops
/// - Parameters are inherited from the ancestor query; main query parameters override CTE parameters
///
///
///
public class WithClause : SqlClause, IWithClause
{
private SqlClauses? _sql;
private IQueryBreakdown? _query;
///
/// Gets or sets the table name for the CTE.
///
public string TableName { get; set; } = string.Empty;
///
/// Gets or sets the parsed SQL clauses representing the CTE query.
/// When getting, if Query is not null, returns SqlClauses constructed from the Query's properties.
/// When setting, if Query is not null, applies the clauses to the Query for validation/restructuring.
/// Otherwise stores the value for later use.
///
public SqlClauses? Sql
{
get => Query?.GetClauses() ?? _sql;
set
{
_sql = value;
// If Query is already set and we're setting new Sql clauses, apply them to the Query
if (_query != null && value != null)
{
_query.ApplyClauses(value);
// Clear the stored value since it's now in the Query
_sql = null;
}
}
}
///
/// Gets or sets the query breakdown representing the CTE.
/// This provides access to the full query structure including parameters.
/// When setting, if Sql clauses were previously set, they will be applied to the Query.
///
public IQueryBreakdown? Query
{
get => _query;
set
{
_query = value;
// If we have stored SQL clauses and a new Query is being set, apply the clauses to it
if (_query != null && _sql != null)
{
_query.ApplyClauses(_sql);
// Clear the stored SQL clauses since they're now part of the Query
_sql = null;
}
}
}
///
/// Gets or sets a value indicating whether this CTE is recursive.
/// Recursive CTEs require a UNION ALL pattern with an anchor member and recursive member.
///
///
/// When set to true, you must also provide a RecursiveQuery. The Query property represents the
/// anchor member (base case), while RecursiveQuery represents the recursive member that typically
/// references the CTE's own TableName. Always ensure the recursive query has a proper termination
/// condition to avoid infinite recursion.
///
public bool IsRecursive { get; set; } = false;
///
/// Gets or sets the recursive query (UNION ALL part) for recursive CTEs.
/// This query represents the recursive member that joins back to the CTE.
/// Only applicable when IsRecursive is true.
///
///
///
/// The recursive member typically:
///
/// - References the CTE's TableName in its FROM clause
/// - Includes a JOIN or WHERE condition that advances the recursion
/// - Has a termination condition (e.g., depth limit, no more rows to process)
/// - Returns the same column count and compatible types as the anchor member
///
///
///
/// Example recursive scenario: traversing an organizational hierarchy where employees reference their managers.
///
///
public IQueryBreakdown? RecursiveQuery { get; set; }
///
/// Gets or sets the explicit column list for the CTE.
/// When specified, defines column names for the CTE that can differ from the underlying query columns.
/// Example: WITH users (id, name, email) AS (...)
///
///
/// Particularly useful for:
///
/// - Recursive CTEs where consistent column naming is critical
/// - CTEs with complex expressions where column aliases may not be clear
/// - Providing meaningful column names for external consumers of the CTE
///
/// The number of column names must match the number of columns in the SELECT clause.
///
public List? ColumnList { get; set; }
///
/// Initializes a new instance of the class.
///
public WithClause()
{
}
///
/// Initializes a new instance of the class with a table name and query.
///
/// The table name for the CTE.
/// The query breakdown for the CTE.
public WithClause(string tableName, IQueryBreakdown query)
{
TableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
Query = query ?? throw new ArgumentNullException(nameof(query));
}
///
/// Initializes a new instance of the class with a table name and SQL clauses.
///
/// The table name for the CTE.
/// The parsed SQL clauses for the CTE.
public WithClause(string tableName, SqlClauses sql)
{
TableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
Sql = sql ?? throw new ArgumentNullException(nameof(sql));
}
}