using System.Collections; using System.Text; using Strata.SqlTools.SqlBreakdown.Interfaces; namespace Strata.SqlTools.SqlBreakdown.Classes; /// /// Base class for SQL query breakdowns that provides common setup/finish clause handling and cloning. /// public abstract class SqlBreakdownBase : ISqlBreakdown { /// /// Initializes a new instance of the class. /// protected SqlBreakdownBase() { SetupClauses = []; FinishClauses = []; } /// /// Gets or sets the setup clauses to execute before the main query. /// public List SetupClauses { get; set; } /// /// Gets a value indicating whether setup clauses are being used. /// public bool IsUsingSetupClause => SetupClauses.Count > 0; /// /// Gets or sets the raw/original SQL statement before parsing and breakdown. /// /// /// This property stores the original SQL text that was parsed to create this breakdown. /// It's useful for auditing, logging, and batch statement retrieval. /// public string? RawSql { get; set; } /// /// Gets or sets the finish clauses to execute after the main query. /// public ArrayList FinishClauses { get; set; } /// /// Gets a value indicating whether finish clauses are being used. /// public bool IsUsingFinishClause => FinishClauses.Count > 0; /// /// Gets the SQL breakdown as a string. Must be implemented by derived classes. /// /// The SQL query string. protected abstract string GetSqlBreakdown(); /// /// Appends a SQL fragment to an with the given logical operation /// (e.g. "and" / "or"), and optionally merges an associated comment. /// If the target clause is empty, the fragment is set as the clause; otherwise it is /// joined with the operation. /// /// The clause being built up (e.g. WhereClause, HavingClause). /// The already-generated SQL fragment to append. /// The logical operation used to join with the existing content. /// Optional comment to merge into clause.Comment. protected static void AppendToClause(ISqlClause clause, string sql, string operation, string? comment) { if (string.IsNullOrWhiteSpace(clause.Clause)) { clause.Clause = sql; } else { clause.Clause = $"{clause.Clause} {operation} {sql}"; } if (string.IsNullOrWhiteSpace(comment)) { return; } if (string.IsNullOrWhiteSpace(clause.Comment)) { clause.Comment = comment; } else { clause.Comment = $"{clause.Comment} {comment}"; } } /// /// Gets a LINQ to SQL query of the specified type based on this breakdown. /// /// The entity type for the query. /// An IQueryable of the specified type; an empty queryable if the breakdown cannot be converted to a LINQ query. /// /// This method allows derived breakdown classes to reconstruct or generate LINQ queries /// from the analyzed components (SELECT, WHERE, ORDER BY, etc.). /// The default implementation returns an empty queryable. /// public virtual IQueryable GetQuery() where T : class => Enumerable.Empty().AsQueryable(); /// /// Gets the complete SQL query including optional setup and finish clauses. /// /// Whether to include setup and finish clauses. /// The complete SQL query string. public string GetSql(bool includeSetupFinish = true) { var sb = new StringBuilder(); if (includeSetupFinish) { // Write out any setup clauses sb.AppendLine(); foreach (string setup in SetupClauses) { sb.AppendLine(setup); } sb.AppendLine(); } sb.Append(GetSqlBreakdown()); if (includeSetupFinish) { // Write out any finish clauses sb.AppendLine(); sb.AppendLine(); foreach (string finish in FinishClauses) { sb.AppendLine(finish); } } return sb.ToString(); } /// /// Returns the SQL query as a string. /// /// The SQL query string. public override string ToString() => GetSql(); /// /// Creates a deep clone of this object. /// /// A cloned instance. public object Clone() { // Create a shallow copy var clone = (SqlBreakdownBase)MemberwiseClone(); // Deep copy the SetupClauses list clone.SetupClauses = new List(SetupClauses); // Deep copy the FinishClauses ArrayList clone.FinishClauses = new ArrayList(FinishClauses); return clone; } }