【问题标题】:how to catch specific pyodbc error message如何捕获特定的pyodbc错误消息
【发布时间】:2012-07-08 17:07:04
【问题描述】:

我试了一下下面的代码,

import pyodbc
try:
    pyodbc.connect('DRIVER={%s};SERVER=%s;DATABASE=%s;UID=%s;PWD=%s' % (driver, server, database, uid, password))
except pyodbc.Error, err:
    logging.warn(err)

我得到的错误信息格式是

('HY000', "[HY000] [MySQL][ODBC 5.1 Driver]Access denied for user 'root'@'192.168.2.27' (using password: YES) (1045) (SQLDriverConnect)")

我只想接收错误的消息部分,即

Access denied for user 'root'@'192.168.2.27'(using password: YES)

我不知道我是否可以特别捕获错误,例如找不到驱动程序、主机关闭等。

我还尝试将错误捕获为:

 except pyodbc.OperationalError, err:
    logging.warn(err)
except pyodbc.DataError, err:
    logging.warn(err)
except pyodbc.IntegrityError, err:
    logging.warn(err)
except pyodbc.ProgrammingError, err:
    logging.warn(err)
except pyodbc.NotSupportedError, err:
    logging.warn(err)
except pyodbc.DatabaseError, err:
    logging.warn(err)
except pyodbc.Error, err:
    logging.warn(err)

但最后一个总是捕获错误。

此外,我看到 pyodbc.Error.message 始终为空。 我怎样才能得到错误中的消息。

谢谢

【问题讨论】:

    标签: python odbc pyodbc


    【解决方案1】:

    这对我有用。

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

    有不同的 SQLSTATES,您可以使用 if-else 语句来打印原因。

    同样,

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

    会给你错误的第二部分和描述。 例如ex.args[0] 给你28000ex.args[1] 给你[28000] LDAP authentication failed for user 'user' (24) (SQLDriverConnect)

    然后,您可以在那里使用字符串操作技术来打印出您想要的内容。希望这可以帮助。

    【讨论】:

    • 这也适用于pypyodbc,当您将pyodbc 替换为正确的库名称时。
    【解决方案2】:

    pyodbc 似乎只是包装了底层 ODBC 实现中的错误/异常,因此您不太可能这样做。

    【讨论】:

      【解决方案3】:

      在 pyodbc 3.0.7 中,它可以很好地捕获 pyodbc.ProgrammingError (可能还有其他错误类型,尽管我没有尝试过)。不过,错误的内容仍然有点神秘,因此可能很难对错误进行更细粒度的处理。

      【讨论】:

        【解决方案4】:

        自从 op 提出这个问题已经很久了,但是这里有一段代码可以将 pyodbc 错误消息解析为可以使用的漂亮 Python 异常。这也是为了扩展,我没有处理所有可能的 sqlserver 错误代码。

        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
        
        

        【讨论】:

        • m = re.match() 更改为 m = re.search() 并且效果很好。谢谢。
        【解决方案5】:

        这将在使用 myodbc 连接到 mssql 时为您提供更清晰易读的错误消息:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-01-08
          • 1970-01-01
          • 1970-01-01
          • 2018-01-16
          • 2012-11-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多