using System.Collections; using System.Text; using Strata.SqlTools.SqlBreakdown.Classes; using Strata.SqlTools.Statements.SqlServer; namespace Strata.SqlTools.Breakdowns.SqlServer; /// /// Represents a DELETE SQL statement breakdown with FROM and WHERE clauses for SQL Server. /// public class DeleteBreakdown : SqlBreakdownBase { protected readonly StatementParser Parser; /// /// Initializes a new instance of the class. /// public DeleteBreakdown() { Parser = new StatementParser(); FromClause = new SqlClause(); WhereClause = new SqlClause(); DeleteClause = new SqlClause(); } /// /// Initializes a new instance of the class. /// /// The FROM clause. /// The WHERE clause. public DeleteBreakdown(string fromClause, string whereClause) : this() { var cleanFrom = Parser.ExtractSqlComments(fromClause, out var fromComments); FromClause.Clause = cleanFrom.Trim(); FromClause.Comment = fromComments.Count > 0 ? string.Join(" ", fromComments) : null; var cleanWhere = Parser.ExtractSqlComments(whereClause, out var whereComments); WhereClause.Clause = cleanWhere.Trim(); WhereClause.Comment = whereComments.Count > 0 ? string.Join(" ", whereComments) : null; } /// /// Gets or sets the FROM clause. /// public SqlClause FromClause { get; set; } /// /// Gets or sets the DELETE clause (optional, for DELETE with alias). /// public SqlClause DeleteClause { get; set; } /// /// Gets a value indicating whether a WHERE clause is being used. /// public bool IsUsingWhereClause => !string.IsNullOrWhiteSpace(WhereClause.Clause); /// /// Gets or sets the WHERE clause. /// public SqlClause WhereClause { get; set; } /// /// Gets the SQL breakdown as a string. /// /// The DELETE SQL statement. protected override string GetSqlBreakdown() { var sb = new StringBuilder(); sb.AppendLine("DELETE "); if (!string.IsNullOrWhiteSpace(DeleteClause.Clause)) { sb.AppendLine($" {DeleteClause.Clause}"); } sb.AppendLine("FROM "); sb.AppendLine($" {FromClause.Clause}"); if (IsUsingWhereClause) { sb.AppendLine("WHERE "); sb.AppendLine($" {WhereClause.Clause}"); } return sb.ToString(); } #region Parse Methods /// /// Parses a DELETE SQL statement into a DeleteBreakdown object. /// /// The DELETE SQL statement to parse. /// A DeleteBreakdown object representing the parsed statement. /// Thrown when sql is null or empty. /// Thrown when the SQL statement cannot be parsed. public static DeleteBreakdown Parse(string sql) { if (string.IsNullOrWhiteSpace(sql)) { throw new ArgumentNullException(nameof(sql), "SQL statement cannot be null or empty."); } if (!TryParse(sql, out var result, out var error)) { throw new FormatException($"Failed to parse DELETE statement: {error}"); } return result; } /// /// Attempts to parse a DELETE SQL statement into a DeleteBreakdown object. /// /// The DELETE SQL statement to parse. /// When this method returns, contains the parsed DeleteBreakdown if successful, or null if parsing failed. /// true if the SQL was successfully parsed; otherwise, false. public static bool TryParse(string sql, out DeleteBreakdown result) => TryParse(sql, out result, out _); /// /// Attempts to parse a DELETE SQL statement into a DeleteBreakdown object. /// /// The DELETE SQL statement to parse. /// When this method returns, contains the parsed DeleteBreakdown if successful, or null if parsing failed. /// When this method returns false, contains a message describing why parsing failed. /// true if the SQL was successfully parsed; otherwise, false. public static bool TryParse(string sql, out DeleteBreakdown result, out string errorMessage) { result = null!; errorMessage = null!; try { return TryParseCore(sql, out result, out errorMessage); } catch (Exception ex) { errorMessage = $"Unexpected error during parsing: {ex.Message}"; return false; } } private static bool TryParseCore(string sql, out DeleteBreakdown result, out string errorMessage) { result = null!; errorMessage = null!; var parser = new StatementParser(); if (!Strata.SqlTools.Statements.SqlServer.ParsePreparation.TryRunPrelude( sql, parser, @"^\s*DELETE\b", "DELETE", out sql, out var setupClauses, out var finishClauses, out errorMessage)) { return false; } // Parse DELETE statement using regex // Pattern: DELETE [table_alias] FROM table WHERE condition var deleteMatch = System.Text.RegularExpressions.Regex.Match(sql, @"DELETE\s+(.*?)\s+FROM\s+(.*?)(?:\s+WHERE\s+(.*))?$", System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Singleline, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout); if (deleteMatch.Success) { var deleteClause = deleteMatch.Groups[1].Value.Trim(); var fromClause = deleteMatch.Groups[2].Value.Trim(); var whereClause = deleteMatch.Groups.Count > 3 ? deleteMatch.Groups[3].Value.Trim() : string.Empty; result = new DeleteBreakdown(fromClause, whereClause) { SetupClauses = setupClauses, FinishClauses = finishClauses }; ApplyDeleteClauseComments(result, parser, deleteClause); return true; } // Try simpler pattern: DELETE FROM table WHERE condition deleteMatch = System.Text.RegularExpressions.Regex.Match(sql, @"DELETE\s+FROM\s+(.*?)(?:\s+WHERE\s+(.*))?$", System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Singleline, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout); if (!deleteMatch.Success) { errorMessage = "Could not parse DELETE statement. Expected format: DELETE [alias] FROM table [WHERE condition]"; return false; } var simpleFromClause = deleteMatch.Groups[1].Value.Trim(); var simpleWhereClause = deleteMatch.Groups.Count > 2 ? deleteMatch.Groups[2].Value.Trim() : string.Empty; result = new DeleteBreakdown(simpleFromClause, simpleWhereClause) { SetupClauses = setupClauses, FinishClauses = finishClauses }; return true; } /// /// Extracts inline comments from the DELETE clause text and applies the cleaned value and /// combined comment to . No-op when the clause is blank. /// private static void ApplyDeleteClauseComments(DeleteBreakdown result, StatementParser parser, string deleteClause) { if (string.IsNullOrWhiteSpace(deleteClause)) { return; } var cleanDelete = parser.ExtractSqlComments(deleteClause, out var deleteComments); result.DeleteClause.Clause = cleanDelete.Trim(); result.DeleteClause.Comment = deleteComments.Count > 0 ? string.Join(" ", deleteComments) : null; } #endregion }