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 }