how to catch specific pyodbc error message

前端 未结 5 1082
北恋
北恋 2020-12-18 21:39

I trid the following code,

import pyodbc
try:
    pyodbc.connect(\'DRIVER={%s};SERVER=%s;DATABASE=%s;UID=%s;PWD=%s\' % (driver, server, database, uid, passwo         


        
相关标签:
5条回答
  • 2020-12-18 21:52

    this will give you more clear and readable error message when connecting to mssql using myodbc:

    try:
        cnxn = pyodbc.connect(...)
    except pyodbc.Error as ex:
        sqlstate = ex.args[1]
        sqlstate = sqlstate.split(".")
        print(sqlstate[-3])
    
    0 讨论(0)
  • 2020-12-18 21:55

    pyodbc seems to just wrap the errors/exceptions from the underlying ODBC implementation, so it's unlikely that you will be able to do this.

    0 讨论(0)
  • 2020-12-18 22:04

    It's been very long since op asked this question, but here goes a snippet of code to parse out pyodbc error messages into nice Python exceptions that can be used. This is also meant to be extended, I didn't handle every possible sqlserver error code.

    import re
    from enum import Enum, IntEnum, unique
    
    
    class PyODBCError(Exception):
        """
        Handle errors for PyODBC. Offers a error message parser
        to apply specific logic depending on the error raise
    
        ODBC error identifier: 23000
    
        pyodbc_error_message (str) -- message raised by PyODBC
            Example:
                [23000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server] \
                Cannot insert explicit value for identity column in table \
                'building' when IDENTITY_INSERT is set to OFF.
                (544) (SQLExecDirectW) \
        """
    
        error_pattern = re.compile(
            r"\[(?P<error_id>.*?)\] \[(?P<operator>.*?)\]\[(?P<driver>.*?)\]\[(?P<database_type>.*?)\](?P<error_message>.+?(?= \()) \((?P<sql_server_error_id>\d*?)\) \(SQLExecDirectW\)"
        )
        sql_error_code_pattern = re.compile(r"\((?P<sql_server_error_code>\d*?)\) \(SQLExecDirectW\)")
        column_pattern = re.compile(r"column \'(?P<column_name>.+?)\'")
        table_pattern = re.compile(r"table \'(?P<table_name>.+?)\'")
        pyodbc_error_code = 'HY000'
    
        def __init__(self, pyodbc_error_message: str) -> None:
            self._parse_error_message(pyodbc_error_message)
    
        def __str__(self) -> str:
            return self.error_message
    
        def _parse_error_message(self, pyodbc_error_message: str) -> None:
            m = re.match(self.error_pattern, pyodbc_error_message)
            self.operator = m.group('operator')
            self.error_id = m.group('error_id')
            self.driver = m.group('driver')
            self.database_type = m.group('database_type')
            self.error_message = m.group('error_message')
            self.sql_server_error_id = m.group('sql_server_error_id')
    
        @classmethod
        def get_message(cls, pyodbc_exception: Exception) -> str:
            if pyodbc_exception.args[1] == cls.pyodbc_error_code:
                return pyodbc_exception.args[0]
            else:
                return pyodbc_exception.args[1]
    
        @classmethod
        def get_pyodbc_code(cls, pyodbc_exception: Exception) -> str:
            if pyodbc_exception.args[1] == cls.pyodbc_error_code:
                return pyodbc_exception.args[1]
            else:
                return pyodbc_exception.args[0]
    
        @staticmethod
        def get_exception(error_code: int):
            return {
                515: IdentityInsertNull,
                544: IdentityInsertSetToOff,
                2627: PrimaryKeyViolation,
                8114: FailedTypeConversion,
                102: IncorrectSyntax,
                32: InvalidNumberParametersSupplied
            }.get(error_code, DefaultException)
    
        @classmethod
        def get_sql_server_error_code(cls, pyodbc_code: str, message: str) -> int:
            """
            Parses error message raised by PyODBC and return SQL Server Error Code
    
            Looks for the following pattern:
                (544) (SQLExecDirectW) -> 544
    
            Args:
                pyodbc_error_message (str): Error string raised by PyODBC
    
            Returns:
                (int) - SQL Server Error Code
            """
    
            if pyodbc_code == cls.pyodbc_error_code:
                return 32
            else:
                m = re.search(cls.sql_error_code_pattern, message)
                if m:
                    return int(m.group('sql_server_error_code'))
                else:
                    raise ValueError(f"Error raised is not from SQL Server: {message}")
    
        @classmethod
        def build_pyodbc_exception(cls, pyodbc_exception: Exception):
            pyodbc_code = cls.get_pyodbc_code(pyodbc_exception)
            error_message = cls.get_message(pyodbc_exception)
            error_code = cls.get_sql_server_error_code(pyodbc_code, error_message)
            exception = cls.get_exception(error_code)
            raise exception(error_message)
    class IdentityInsertNull(PyODBCError):
        """
        Handle specific PyODBC error related to Null Value Inserted on Identity Column
        """
    
        def __init__(self, pyodbc_error_message):
            super().__init__(pyodbc_error_message)
            m = re.search(self.table_pattern, self.error_message)
            self.table_name = m.group('table_name')
            m = re.search(self.column_pattern, self.error_message)
            self.column_name = m.group('column_name')
    
    
    class IdentityInsertSetToOff(PyODBCError):
        """
        Handle specific PyODBC error related to Identity Not Set to On/Off
        """
    
        def __init__(self, pyodbc_error_message):
            super().__init__(pyodbc_error_message)
            m = re.search(self.table_pattern, self.error_message)
            self.table_name = m.group('table_name')
    
    
    class FailedTypeConversion(PyODBCError):
        """
        Handle specific PyODBC error related to data type conversion
        """
    
        def __init__(self, pyodbc_error_message):
            super().__init__(pyodbc_error_message)
    
    
    class PrimaryKeyViolation(PyODBCError):
        """
        Handle specific PyODBC error related to Primary Key Violation
        """
    
        def __init__(self, pyodbc_error_message):
            super().__init__(pyodbc_error_message)
    
    
    class IncorrectSyntax(PyODBCError):
        """
        Handle specific PyODBC error related to incorrect syntax in query
        """
    
        def __init__(self, pyodbc_error_message):
            super().__init__(pyodbc_error_message)
    
    
    class DefaultException(PyODBCError):
        """
        Handle default PyODBC errors
        """
    
        def __init__(self, pyodbc_error_message):
            super().__init__(pyodbc_error_message)
    
        def __str__(self) -> str:
            return f"{self.sql_server_error_id} - {self.error_message}"
    
    
    class InvalidNumberParametersSupplied(Exception):
        def __init__(self, error_message) -> None:
            self.message = error_message
    
        def __str__(self) -> str:
            return self.message
    
    
    0 讨论(0)
  • 2020-12-18 22:08

    In pyodbc 3.0.7, it works fine to catch pyodbc.ProgrammingError (and presumably the other error types, although I haven't tried). The contents of the error are still sort of cryptic, though, so it may be hard to do finer-grained handling of errors.

    0 讨论(0)
  • 2020-12-18 22:12

    This worked for me.

        try:
            cnxn = pyodbc.connect(...)
        except pyodbc.Error as ex:
            sqlstate = ex.args[0]
            if sqlstate == '28000':
                print("LDAP Connection failed: check password")
    

    There are different SQLSTATES and you can have if-else statements to print out the cause.

    Similarly,

      try:
            cnxn = pyodbc.connect(...)
      except pyodbc.Error as ex:
            sqlstate = ex.args[1]
            print(sqlstate) 
    

    will give you the second part of the error with description. For exampleex.args[0] give you 28000 and ex.args[1] gives [28000] LDAP authentication failed for user 'user' (24) (SQLDriverConnect)

    You can then use String manipulation techniques there to just print out what you want. Hope this helps.

    0 讨论(0)
提交回复
热议问题