sqlglot.errors
1from __future__ import annotations 2 3import typing as t 4from enum import auto 5 6from sqlglot.helper import AutoName 7 8 9# ANSI escape codes for error formatting 10ANSI_UNDERLINE = "\033[4m" 11ANSI_RESET = "\033[0m" 12ERROR_MESSAGE_CONTEXT_DEFAULT = 100 13 14 15class ErrorLevel(AutoName): 16 IGNORE = auto() 17 """Ignore all errors.""" 18 19 WARN = auto() 20 """Log all errors.""" 21 22 RAISE = auto() 23 """Collect all errors and raise a single exception.""" 24 25 IMMEDIATE = auto() 26 """Immediately raise an exception on the first error found.""" 27 28 29class SqlglotError(Exception): 30 pass 31 32 33class UnsupportedError(SqlglotError): 34 pass 35 36 37class ParseError(SqlglotError): 38 def __init__( 39 self, 40 message: str, 41 errors: t.Optional[t.List[t.Dict[str, t.Any]]] = None, 42 ): 43 super().__init__(message) 44 self.errors = errors or [] 45 46 @classmethod 47 def new( 48 cls, 49 message: str, 50 description: t.Optional[str] = None, 51 line: t.Optional[int] = None, 52 col: t.Optional[int] = None, 53 start_context: t.Optional[str] = None, 54 highlight: t.Optional[str] = None, 55 end_context: t.Optional[str] = None, 56 into_expression: t.Optional[str] = None, 57 ) -> ParseError: 58 return cls( 59 message, 60 [ 61 { 62 "description": description, 63 "line": line, 64 "col": col, 65 "start_context": start_context, 66 "highlight": highlight, 67 "end_context": end_context, 68 "into_expression": into_expression, 69 } 70 ], 71 ) 72 73 74class TokenError(SqlglotError): 75 pass 76 77 78class OptimizeError(SqlglotError): 79 pass 80 81 82class SchemaError(SqlglotError): 83 pass 84 85 86class ExecuteError(SqlglotError): 87 pass 88 89 90def highlight_sql( 91 sql: str, 92 positions: t.List[t.Tuple[int, int]], 93 context_length: int = ERROR_MESSAGE_CONTEXT_DEFAULT, 94) -> t.Tuple[str, str, str, str]: 95 """ 96 Highlight a SQL string using ANSI codes at the given positions. 97 98 Args: 99 sql: The complete SQL string. 100 positions: List of (start, end) tuples where both start and end are inclusive 0-based 101 indexes. For example, to highlight "foo" in "SELECT foo", use (7, 9). 102 The positions will be sorted and de-duplicated if they overlap. 103 context_length: Number of characters to show before the first highlight and after 104 the last highlight. 105 106 Returns: 107 A tuple of (formatted_sql, start_context, highlight, end_context) where: 108 - formatted_sql: The SQL with ANSI underline codes applied to highlighted sections 109 - start_context: Plain text before the first highlight 110 - highlight: Plain text from the first highlight start to the last highlight end, 111 including any non-highlighted text in between (no ANSI) 112 - end_context: Plain text after the last highlight 113 114 Note: 115 If positions is empty, raises a ValueError. 116 """ 117 if not positions: 118 raise ValueError("positions must contain at least one (start, end) tuple") 119 120 start_context = "" 121 end_context = "" 122 first_highlight_start = 0 123 formatted_parts = [] 124 previous_part_end = 0 125 sorted_positions = sorted(positions, key=lambda pos: pos[0]) 126 127 if sorted_positions[0][0] > 0: 128 first_highlight_start = sorted_positions[0][0] 129 start_context = sql[max(0, first_highlight_start - context_length) : first_highlight_start] 130 formatted_parts.append(start_context) 131 previous_part_end = first_highlight_start 132 133 for start, end in sorted_positions: 134 highlight_start = max(start, previous_part_end) 135 highlight_end = end + 1 136 if highlight_start >= highlight_end: 137 continue # Skip invalid or overlapping highlights 138 if highlight_start > previous_part_end: 139 formatted_parts.append(sql[previous_part_end:highlight_start]) 140 formatted_parts.append(f"{ANSI_UNDERLINE}{sql[highlight_start:highlight_end]}{ANSI_RESET}") 141 previous_part_end = highlight_end 142 143 if previous_part_end < len(sql): 144 end_context = sql[previous_part_end : previous_part_end + context_length] 145 formatted_parts.append(end_context) 146 147 formatted_sql = "".join(formatted_parts) 148 highlight = sql[first_highlight_start:previous_part_end] 149 150 return formatted_sql, start_context, highlight, end_context 151 152 153def concat_messages(errors: t.Sequence[t.Any], maximum: int) -> str: 154 msg = [str(e) for e in errors[:maximum]] 155 remaining = len(errors) - maximum 156 if remaining > 0: 157 msg.append(f"... and {remaining} more") 158 return "\n\n".join(msg) 159 160 161def merge_errors(errors: t.Sequence[ParseError]) -> t.List[t.Dict[str, t.Any]]: 162 return [e_dict for error in errors for e_dict in error.errors]
ANSI_UNDERLINE =
'\x1b[4m'
ANSI_RESET =
'\x1b[0m'
ERROR_MESSAGE_CONTEXT_DEFAULT =
100
16class ErrorLevel(AutoName): 17 IGNORE = auto() 18 """Ignore all errors.""" 19 20 WARN = auto() 21 """Log all errors.""" 22 23 RAISE = auto() 24 """Collect all errors and raise a single exception.""" 25 26 IMMEDIATE = auto() 27 """Immediately raise an exception on the first error found."""
An enumeration.
IMMEDIATE =
<ErrorLevel.IMMEDIATE: 'IMMEDIATE'>
Immediately raise an exception on the first error found.
class
SqlglotError(builtins.Exception):
Common base class for all non-exit exceptions.
Common base class for all non-exit exceptions.
38class ParseError(SqlglotError): 39 def __init__( 40 self, 41 message: str, 42 errors: t.Optional[t.List[t.Dict[str, t.Any]]] = None, 43 ): 44 super().__init__(message) 45 self.errors = errors or [] 46 47 @classmethod 48 def new( 49 cls, 50 message: str, 51 description: t.Optional[str] = None, 52 line: t.Optional[int] = None, 53 col: t.Optional[int] = None, 54 start_context: t.Optional[str] = None, 55 highlight: t.Optional[str] = None, 56 end_context: t.Optional[str] = None, 57 into_expression: t.Optional[str] = None, 58 ) -> ParseError: 59 return cls( 60 message, 61 [ 62 { 63 "description": description, 64 "line": line, 65 "col": col, 66 "start_context": start_context, 67 "highlight": highlight, 68 "end_context": end_context, 69 "into_expression": into_expression, 70 } 71 ], 72 )
Common base class for all non-exit exceptions.
@classmethod
def
new( cls, message: str, description: Optional[str] = None, line: Optional[int] = None, col: Optional[int] = None, start_context: Optional[str] = None, highlight: Optional[str] = None, end_context: Optional[str] = None, into_expression: Optional[str] = None) -> ParseError:
47 @classmethod 48 def new( 49 cls, 50 message: str, 51 description: t.Optional[str] = None, 52 line: t.Optional[int] = None, 53 col: t.Optional[int] = None, 54 start_context: t.Optional[str] = None, 55 highlight: t.Optional[str] = None, 56 end_context: t.Optional[str] = None, 57 into_expression: t.Optional[str] = None, 58 ) -> ParseError: 59 return cls( 60 message, 61 [ 62 { 63 "description": description, 64 "line": line, 65 "col": col, 66 "start_context": start_context, 67 "highlight": highlight, 68 "end_context": end_context, 69 "into_expression": into_expression, 70 } 71 ], 72 )
Common base class for all non-exit exceptions.
Common base class for all non-exit exceptions.
Common base class for all non-exit exceptions.
Common base class for all non-exit exceptions.
def
highlight_sql( sql: str, positions: List[Tuple[int, int]], context_length: int = 100) -> Tuple[str, str, str, str]:
91def highlight_sql( 92 sql: str, 93 positions: t.List[t.Tuple[int, int]], 94 context_length: int = ERROR_MESSAGE_CONTEXT_DEFAULT, 95) -> t.Tuple[str, str, str, str]: 96 """ 97 Highlight a SQL string using ANSI codes at the given positions. 98 99 Args: 100 sql: The complete SQL string. 101 positions: List of (start, end) tuples where both start and end are inclusive 0-based 102 indexes. For example, to highlight "foo" in "SELECT foo", use (7, 9). 103 The positions will be sorted and de-duplicated if they overlap. 104 context_length: Number of characters to show before the first highlight and after 105 the last highlight. 106 107 Returns: 108 A tuple of (formatted_sql, start_context, highlight, end_context) where: 109 - formatted_sql: The SQL with ANSI underline codes applied to highlighted sections 110 - start_context: Plain text before the first highlight 111 - highlight: Plain text from the first highlight start to the last highlight end, 112 including any non-highlighted text in between (no ANSI) 113 - end_context: Plain text after the last highlight 114 115 Note: 116 If positions is empty, raises a ValueError. 117 """ 118 if not positions: 119 raise ValueError("positions must contain at least one (start, end) tuple") 120 121 start_context = "" 122 end_context = "" 123 first_highlight_start = 0 124 formatted_parts = [] 125 previous_part_end = 0 126 sorted_positions = sorted(positions, key=lambda pos: pos[0]) 127 128 if sorted_positions[0][0] > 0: 129 first_highlight_start = sorted_positions[0][0] 130 start_context = sql[max(0, first_highlight_start - context_length) : first_highlight_start] 131 formatted_parts.append(start_context) 132 previous_part_end = first_highlight_start 133 134 for start, end in sorted_positions: 135 highlight_start = max(start, previous_part_end) 136 highlight_end = end + 1 137 if highlight_start >= highlight_end: 138 continue # Skip invalid or overlapping highlights 139 if highlight_start > previous_part_end: 140 formatted_parts.append(sql[previous_part_end:highlight_start]) 141 formatted_parts.append(f"{ANSI_UNDERLINE}{sql[highlight_start:highlight_end]}{ANSI_RESET}") 142 previous_part_end = highlight_end 143 144 if previous_part_end < len(sql): 145 end_context = sql[previous_part_end : previous_part_end + context_length] 146 formatted_parts.append(end_context) 147 148 formatted_sql = "".join(formatted_parts) 149 highlight = sql[first_highlight_start:previous_part_end] 150 151 return formatted_sql, start_context, highlight, end_context
Highlight a SQL string using ANSI codes at the given positions.
Arguments:
- sql: The complete SQL string.
- positions: List of (start, end) tuples where both start and end are inclusive 0-based indexes. For example, to highlight "foo" in "SELECT foo", use (7, 9). The positions will be sorted and de-duplicated if they overlap.
- context_length: Number of characters to show before the first highlight and after the last highlight.
Returns:
A tuple of (formatted_sql, start_context, highlight, end_context) where:
- formatted_sql: The SQL with ANSI underline codes applied to highlighted sections
- start_context: Plain text before the first highlight
- highlight: Plain text from the first highlight start to the last highlight end, including any non-highlighted text in between (no ANSI)
- end_context: Plain text after the last highlight
Note:
If positions is empty, raises a ValueError.
def
concat_messages(errors: Sequence[Any], maximum: int) -> str: