using System.Text; namespace Strata.SqlTools.SqlServer.Exceptions; /// /// Exception thrown when SQL parsing fails. /// Provides detailed context about the parse failure including position and surrounding text. /// public class SqlParseException : Exception { /// /// Gets the position in the SQL string where the parse error occurred. /// public int Position { get; } /// /// Gets the SQL statement that failed to parse. /// public string Sql { get; } /// /// Gets the text near the error position (up to 40 characters). /// public string NearText { get; } /// /// Initializes a new instance of the class. /// /// The error message describing the parse failure. /// The SQL statement that failed to parse. /// The position in the SQL where the error occurred. /// /// /// throw new SqlParseException( /// "Unexpected token 'FROM'", /// "SELECT * FRM users", /// 10 /// ); /// // Message will include: /// // - Error description /// // - Position: 10 /// // - Near: "* FRM users" /// // - Full SQL statement /// /// public SqlParseException(string message, string sql, int position) : base(FormatMessage(message, sql, position)) { Position = position; Sql = sql ?? string.Empty; NearText = ExtractNearText(Sql, position); } /// /// Initializes a new instance of the class with an inner exception. /// /// The error message describing the parse failure. /// The SQL statement that failed to parse. /// The position in the SQL where the error occurred. /// The exception that caused this parse failure. public SqlParseException(string message, string sql, int position, Exception innerException) : base(FormatMessage(message, sql, position), innerException) { Position = position; Sql = sql ?? string.Empty; NearText = ExtractNearText(Sql, position); } private static string FormatMessage(string message, string sql, int position) { if (string.IsNullOrEmpty(sql)) { return $"{message}\nSQL statement is empty or null."; } var nearText = ExtractNearText(sql, position); var sb = new StringBuilder(); sb.AppendLine(message); sb.AppendLine($"Position: {position}"); sb.AppendLine($"Near: '{nearText}'"); // Show full SQL for short statements, truncated for long ones if (sql.Length <= 200) { sb.AppendLine($"Full SQL: {sql}"); } else { sb.AppendLine($"SQL (truncated): {sql.Substring(0, 197)}..."); } return sb.ToString(); } private static string ExtractNearText(string sql, int position) { if (string.IsNullOrEmpty(sql)) { return string.Empty; } // Clamp position to valid range position = Math.Max(0, Math.Min(position, sql.Length)); // Extract up to 20 chars before and 20 chars after the position var start = Math.Max(0, position - 20); var length = Math.Min(40, sql.Length - start); var nearText = sql.Substring(start, length); // Add ellipsis if truncated if (start > 0) { nearText = "..." + nearText; } if (start + length < sql.Length) { nearText = nearText + "..."; } return nearText; } }