55from sqlspec .core import DriverParameterProfile , ParameterStyle , StatementConfig , build_statement_config_from_profile
66from sqlspec .exceptions import (
77 CheckViolationError ,
8+ ConnectionTimeoutError ,
89 DatabaseConnectionError ,
910 DataError ,
11+ DeadlockError ,
1012 ForeignKeyViolationError ,
1113 IntegrityError ,
1214 NotNullViolationError ,
15+ PermissionDeniedError ,
16+ QueryTimeoutError ,
1317 SQLParsingError ,
1418 SQLSpecError ,
1519 TransactionError ,
3842 "resolve_rowcount" ,
3943)
4044
45+ # MySQL error codes for constraint violations
4146MYSQL_ER_DUP_ENTRY = 1062
4247MYSQL_ER_NO_DEFAULT_FOR_FIELD = 1364
4348MYSQL_ER_CHECK_CONSTRAINT_VIOLATED = 3819
4449
50+ # MySQL error codes for permission/access errors
51+ MYSQL_ER_DBACCESS_DENIED = 1044
52+ MYSQL_ER_ACCESS_DENIED = 1045
53+ MYSQL_ER_TABLEACCESS_DENIED = 1142
54+
55+ # MySQL error codes for transaction errors
56+ MYSQL_ER_LOCK_WAIT_TIMEOUT = 1205
57+ MYSQL_ER_LOCK_DEADLOCK = 1213
58+
59+ # MySQL error codes for connection errors
60+ MYSQL_CR_CONNECTION_ERROR = 2002
61+ MYSQL_CR_CONN_HOST_ERROR = 2003
62+ MYSQL_CR_UNKNOWN_HOST = 2005
63+ MYSQL_CR_SERVER_GONE_ERROR = 2006
64+ MYSQL_CR_SERVER_LOST = 2013
65+
4566
4667def _bool_to_int (value : bool ) -> int :
4768 return int (value )
@@ -171,6 +192,12 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpe
171192 raising. This pattern is more robust for use in __exit__ handlers and
172193 avoids issues with exception control flow in different Python versions.
173194
195+ Mapping priority:
196+ 1. Specific error codes (most reliable for MySQL)
197+ 2. SQLSTATE codes (where available)
198+ 3. Generic error code ranges
199+ 4. Default SQLSpecError fallback
200+
174201 Args:
175202 error: The AsyncMy exception to map
176203 logger: Optional logger for migration warnings
@@ -179,13 +206,16 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpe
179206 True to suppress expected migration errors, or a SQLSpec exception
180207 """
181208 error_code = error .args [0 ] if len (error .args ) >= 1 and isinstance (error .args [0 ], int ) else None
182- sqlstate = error .sqlstate if has_sqlstate (error ) and error .sqlstate is not None else None
209+ sqlstate_attr = error .sqlstate if has_sqlstate (error ) else None
210+ sqlstate = sqlstate_attr if sqlstate_attr is not None else None
183211
212+ # Migration-specific errors to suppress
184213 if error_code in {1061 , 1091 }:
185214 if logger is not None :
186215 logger .warning ("AsyncMy MySQL expected migration error (ignoring): %s" , error )
187216 return True
188217
218+ # Integrity constraint violations
189219 if sqlstate == "23505" or error_code == MYSQL_ER_DUP_ENTRY :
190220 return _create_mysql_error (error , sqlstate , error_code , UniqueViolationError , "unique constraint violation" )
191221 if sqlstate == "23503" or error_code in {1216 , 1217 , 1451 , 1452 }:
@@ -198,20 +228,44 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpe
198228 return _create_mysql_error (error , sqlstate , error_code , CheckViolationError , "check constraint violation" )
199229 if sqlstate and sqlstate .startswith ("23" ):
200230 return _create_mysql_error (error , sqlstate , error_code , IntegrityError , "integrity constraint violation" )
231+
232+ # Permission/access errors (check specific codes first)
233+ if error_code in {MYSQL_ER_DBACCESS_DENIED , MYSQL_ER_ACCESS_DENIED , MYSQL_ER_TABLEACCESS_DENIED }:
234+ return _create_mysql_error (error , sqlstate , error_code , PermissionDeniedError , "access denied" )
235+ if sqlstate and sqlstate .startswith ("28" ):
236+ return _create_mysql_error (error , sqlstate , error_code , PermissionDeniedError , "authorization error" )
237+
238+ # Transaction errors (deadlock vs lock wait timeout)
239+ if error_code == MYSQL_ER_LOCK_DEADLOCK :
240+ return _create_mysql_error (error , sqlstate , error_code , DeadlockError , "deadlock detected" )
241+ if error_code == MYSQL_ER_LOCK_WAIT_TIMEOUT :
242+ return _create_mysql_error (error , sqlstate , error_code , QueryTimeoutError , "lock wait timeout" )
243+ if sqlstate and sqlstate .startswith ("40" ):
244+ return _create_mysql_error (error , sqlstate , error_code , TransactionError , "transaction error" )
245+
246+ # SQL syntax errors
201247 if sqlstate and sqlstate .startswith ("42" ):
202248 return _create_mysql_error (error , sqlstate , error_code , SQLParsingError , "SQL syntax error" )
249+ if error_code in range (1064 , 1100 ):
250+ return _create_mysql_error (error , sqlstate , error_code , SQLParsingError , "SQL syntax error" )
251+
252+ # Connection errors
203253 if sqlstate and sqlstate .startswith ("08" ):
204254 return _create_mysql_error (error , sqlstate , error_code , DatabaseConnectionError , "connection error" )
205- if sqlstate and sqlstate .startswith ("40" ):
206- return _create_mysql_error (error , sqlstate , error_code , TransactionError , "transaction error" )
255+ if error_code == MYSQL_CR_SERVER_LOST :
256+ return _create_mysql_error (error , sqlstate , error_code , ConnectionTimeoutError , "connection lost" )
257+ if error_code in {
258+ MYSQL_CR_CONNECTION_ERROR ,
259+ MYSQL_CR_CONN_HOST_ERROR ,
260+ MYSQL_CR_UNKNOWN_HOST ,
261+ MYSQL_CR_SERVER_GONE_ERROR ,
262+ }:
263+ return _create_mysql_error (error , sqlstate , error_code , DatabaseConnectionError , "connection error" )
264+
265+ # Data errors
207266 if sqlstate and sqlstate .startswith ("22" ):
208267 return _create_mysql_error (error , sqlstate , error_code , DataError , "data error" )
209- if error_code in {2002 , 2003 , 2005 , 2006 , 2013 }:
210- return _create_mysql_error (error , sqlstate , error_code , DatabaseConnectionError , "connection error" )
211- if error_code in {1205 , 1213 }:
212- return _create_mysql_error (error , sqlstate , error_code , TransactionError , "transaction error" )
213- if error_code in range (1064 , 1100 ):
214- return _create_mysql_error (error , sqlstate , error_code , SQLParsingError , "SQL syntax error" )
268+
215269 return _create_mysql_error (error , sqlstate , error_code , SQLSpecError , "database error" )
216270
217271
0 commit comments