using System.Collections;
using System.Text;
using SqlServerDeleteBreakdown = Strata.SqlTools.Breakdowns.SqlServer.DeleteBreakdown;
using StatementParser = Strata.SqlTools.Statements.Snowflake.StatementParser;
namespace Strata.SqlTools.Breakdowns.Snowflake;
///
/// Represents a DELETE SQL statement breakdown with FROM and WHERE clauses for Snowflake.
///
public class DeleteBreakdown : SqlServerDeleteBreakdown
{
private static readonly StatementParser SnowflakeParserInstance = new StatementParser();
///
/// Initializes a new instance of the class.
///
public DeleteBreakdown() : base()
{
}
///
/// Initializes a new instance of the class.
///
/// The FROM clause.
/// The WHERE clause.
/// If true, uses Microsoft T-SQL parsing rules. If false, uses Snowflake parsing rules. Defaults to false.
public DeleteBreakdown(string fromClause, string whereClause, bool isMicrosoftSql = false)
: base()
{
var parser = isMicrosoftSql ? Parser : SnowflakeParserInstance;
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 the SQL breakdown as a string for Snowflake.
///
/// The DELETE SQL statement.
protected override string GetSqlBreakdown()
{
var sb = new StringBuilder();
// Snowflake DELETE syntax is simpler - no DELETE clause with alias
sb.AppendLine("DELETE FROM ");
sb.AppendLine($" {FromClause.Clause}");
if (IsUsingWhereClause)
{
sb.AppendLine("WHERE ");
sb.AppendLine($" {WhereClause.Clause}");
}
return sb.ToString();
}
#region Parse Methods
///
/// Parses a Snowflake DELETE SQL statement into a DeleteBreakdown object.
///
/// The DELETE SQL statement to parse.
/// If true, uses Microsoft T-SQL parsing rules. If false, uses Snowflake parsing rules. Defaults to false.
/// 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, bool isMicrosoftSql = false)
{
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, isMicrosoftSql))
{
throw new FormatException($"Failed to parse {(isMicrosoftSql ? "T-SQL" : "Snowflake SQL")} DELETE statement: {error}");
}
return result;
}
///
/// Attempts to parse a Snowflake 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.
/// If true, uses Microsoft T-SQL parsing rules instead of Snowflake rules. Defaults to false.
/// true if the SQL was successfully parsed; otherwise, false.
public static bool TryParse(string sql, out DeleteBreakdown result, bool isMicrosoftSql = false)
=> TryParse(sql, out result, out _, isMicrosoftSql);
///
/// Attempts to parse a Snowflake DELETE SQL statement into a DeleteBreakdown object.
/// Handles Snowflake-specific syntax.
///
/// 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.
/// If true, uses Microsoft T-SQL parsing rules instead of Snowflake rules. Defaults to false.
/// true if the SQL was successfully parsed; otherwise, false.
public static bool TryParse(string sql, out DeleteBreakdown result, out string errorMessage, bool isMicrosoftSql = false)
{
result = null!;
errorMessage = null!;
try
{
if (string.IsNullOrWhiteSpace(sql))
{
errorMessage = "SQL statement cannot be null or empty.";
return false;
}
// If Microsoft SQL mode, delegate to base class
if (isMicrosoftSql)
{
if (!SqlServerDeleteBreakdown.TryParse(sql, out var baseResult, out errorMessage))
{
return false;
}
// Convert to Snowflake DeleteBreakdown
result = new DeleteBreakdown
{
FromClause = baseResult.FromClause,
WhereClause = baseResult.WhereClause,
DeleteClause = baseResult.DeleteClause,
SetupClauses = baseResult.SetupClauses,
FinishClauses = baseResult.FinishClauses
};
return true;
}
var parser = SnowflakeParserInstance;
sql = parser.NormalizeSqlPreservingComments(sql);
// Check if it's a DELETE statement
var sqlTrimmed = sql.TrimStart();
if (!System.Text.RegularExpressions.Regex.IsMatch(sqlTrimmed, @"^\s*DELETE\b",
System.Text.RegularExpressions.RegexOptions.IgnoreCase, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout))
{
errorMessage = "SQL statement must start with DELETE.";
return false;
}
// Extract setup and finish clauses
var setupClauses = new List();
sql = parser.ExtractSetupClauses(sql, setupClauses);
var finishClauses = new ArrayList();
sql = parser.ExtractFinishClauses(sql, finishClauses);
// Snowflake uses simpler DELETE syntax: DELETE FROM table WHERE condition
var 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 FROM table [WHERE condition]";
return false;
}
var fromClause = deleteMatch.Groups[1].Value.Trim();
var whereClause = deleteMatch.Groups.Count > 2 ? deleteMatch.Groups[2].Value.Trim() : string.Empty;
result = new DeleteBreakdown(fromClause, whereClause, isMicrosoftSql: false)
{
SetupClauses = setupClauses,
FinishClauses = finishClauses
};
return true;
}
catch (Exception ex)
{
errorMessage = $"Unexpected error during parsing: {ex.Message}";
return false;
}
}
#endregion
}