如何捕获特定的pyodbc错误消息

时间:2012-07-09 09:52:59

标签: python odbc pyodbc

我做了以下代码,

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)

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

Fruthermore我看到 pyodbc.Error.message 始终为空。 我怎样才能在错误中得到消息。

由于

5 个答案:

答案 0 :(得分:18)

这对我有用。

    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)

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

答案 1 :(得分:6)

pyodbc似乎只是从底层的ODBC实现中包装错误/异常,所以你不太可能这样做。

答案 2 :(得分:2)

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

答案 3 :(得分:1)

自从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

答案 4 :(得分:0)

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

Deferred