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
}