SonarQube Analysis / sonarqube (pull_request) Successful in 4m42s
The `[Serializable]` attribute and corresponding `[OnDeserialized]` methods have been removed from various breakdown classes. This eliminates reliance on `BinaryFormatter`, which is a deprecated and insecure serialization mechanism in modern .NET. This change also resolves SonarQube rule S5766 warnings by removing the context in which they apply, leading to cleaner and more secure code.
191 lines
8.0 KiB
C#
191 lines
8.0 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Represents a DELETE SQL statement breakdown with FROM and WHERE clauses for Snowflake.
|
|
/// </summary>
|
|
public class DeleteBreakdown : SqlServerDeleteBreakdown
|
|
{
|
|
private static readonly StatementParser SnowflakeParserInstance = new StatementParser();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="DeleteBreakdown"/> class.
|
|
/// </summary>
|
|
public DeleteBreakdown() : base()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="DeleteBreakdown"/> class.
|
|
/// </summary>
|
|
/// <param name="fromClause">The FROM clause.</param>
|
|
/// <param name="whereClause">The WHERE clause.</param>
|
|
/// <param name="isMicrosoftSql">If true, uses Microsoft T-SQL parsing rules. If false, uses Snowflake parsing rules. Defaults to false.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the SQL breakdown as a string for Snowflake.
|
|
/// </summary>
|
|
/// <returns>The DELETE SQL statement.</returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Parses a Snowflake DELETE SQL statement into a DeleteBreakdown object.
|
|
/// </summary>
|
|
/// <param name="sql">The DELETE SQL statement to parse.</param>
|
|
/// <param name="isMicrosoftSql">If true, uses Microsoft T-SQL parsing rules. If false, uses Snowflake parsing rules. Defaults to false.</param>
|
|
/// <returns>A DeleteBreakdown object representing the parsed statement.</returns>
|
|
/// <exception cref="ArgumentNullException">Thrown when sql is null or empty.</exception>
|
|
/// <exception cref="FormatException">Thrown when the SQL statement cannot be parsed.</exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to parse a Snowflake DELETE SQL statement into a DeleteBreakdown object.
|
|
/// </summary>
|
|
/// <param name="sql">The DELETE SQL statement to parse.</param>
|
|
/// <param name="result">When this method returns, contains the parsed DeleteBreakdown if successful, or null if parsing failed.</param>
|
|
/// <param name="isMicrosoftSql">If true, uses Microsoft T-SQL parsing rules instead of Snowflake rules. Defaults to false.</param>
|
|
/// <returns>true if the SQL was successfully parsed; otherwise, false.</returns>
|
|
public static bool TryParse(string sql, out DeleteBreakdown result, bool isMicrosoftSql = false)
|
|
=> TryParse(sql, out result, out _, isMicrosoftSql);
|
|
|
|
/// <summary>
|
|
/// Attempts to parse a Snowflake DELETE SQL statement into a DeleteBreakdown object.
|
|
/// Handles Snowflake-specific syntax.
|
|
/// </summary>
|
|
/// <param name="sql">The DELETE SQL statement to parse.</param>
|
|
/// <param name="result">When this method returns, contains the parsed DeleteBreakdown if successful, or null if parsing failed.</param>
|
|
/// <param name="errorMessage">When this method returns false, contains a message describing why parsing failed.</param>
|
|
/// <param name="isMicrosoftSql">If true, uses Microsoft T-SQL parsing rules instead of Snowflake rules. Defaults to false.</param>
|
|
/// <returns>true if the SQL was successfully parsed; otherwise, false.</returns>
|
|
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<string>();
|
|
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
|
|
}
|