mssql+pyodbc bad version check

Issue #2318 resolved
Former user created an issue

Incorrect python comparison check for pyodbc.version. The check fails silently in python 2 (all), and fails with error in python 3.

Line 218 of dialects/mssql/pyodbc.py (__init__ of class MSDialect_pyodbc):
''self._need_decimal_fix = self.dbapi and \
       tuple(self.dbapi.version.split(".")) < (2, 1, 8)
''

tuple(...) results in a tuple of strings (eg ("2", "1", "8")), which ALWAYS returns False in python 2 when compared to the tuple of integers (2,1,8), and returns error "TypeError: unorderable types: str() < int()" in python 3.

Additionally, pyodbc for python 3 is currently using version strings of the format "py3-3.0.1-beta4", which would cause a problem in any case, but arguably, that is the fault of the pyodbc people for not being consistent with their version strings)

Suggested fix, for all versions of pythons and pyodbc:

''try:
    self._need_decimal_fix = self.dbapi and \
        tuple(map(int, self.dbapi.version.split("."))) < (2, 1, 8)
except:
    self._need_decimal_fix = False
''

Comments (3)

  1. Mike Bayer repo owner
    • changed milestone to 0.7.4
    • marked as critical
    • assigned issue to

    suggested fix + tests would be based on this:

    import re
    
    def parse(vers):
        m = re.match(
                r'(?:py.*-)?([\d\.](\d\.)+)(?:-(\w+))?',
                vers
            )
        if not m:
            return ()
        vers = tuple([for x in m.group(1).split(".")](int(x)))
        if m.group(2):
            vers += (m.group(2),)
        return vers
    
    
    for vers in [   ('2.1.8'),
        ("py3-3.0.1-beta4"),
        ("10.15.17")
    ](
    ):
        print parse(vers)
    

    patch which still needs tests...

    diff -r b746921e78f3890f643ed6581ac01b25f673b476 lib/sqlalchemy/connectors/pyodbc.py
    --- a/lib/sqlalchemy/connectors/pyodbc.py   Wed Nov 02 12:34:55 2011 -0400
    +++ b/lib/sqlalchemy/connectors/pyodbc.py   Sun Nov 06 13:59:51 2011 -0800
    @@ -126,6 +126,23 @@
             # run other initialization which asks for user name, etc.
             super(PyODBCConnector, self).initialize(connection)
    
    +    def _dbapi_version(self):
    +        if not self.dbapi:
    +            return ()
    +        return self._parse_dbapi_version(self.dbapi.version)
    +
    +    def _parse_dbapi_version(self, vers):
    +        m = re.match(
    +                r'(?:py.*-)?([\d\.](\d\.)+)(?:-(\w+))?',
    +                vers
    +            )
    +        if not m:
    +            return ()
    +        vers = tuple([for x in m.group(1).split(".")](int(x)))
    +        if m.group(2):
    +            vers += (m.group(2),)
    +        return vers
    +
         def _get_server_version_info(self, connection):
             dbapi_con = connection.connection
             version = []
    diff -r b746921e78f3890f643ed6581ac01b25f673b476 lib/sqlalchemy/dialects/mssql/pyodbc.py
    --- a/lib/sqlalchemy/dialects/mssql/pyodbc.py   Wed Nov 02 12:34:55 2011 -0400
    +++ b/lib/sqlalchemy/dialects/mssql/pyodbc.py   Sun Nov 06 13:59:51 2011 -0800
    @@ -216,6 +216,6 @@
             self.use_scope_identity = self.dbapi and \
                             hasattr(self.dbapi.Cursor, 'nextset')
             self._need_decimal_fix = self.dbapi and \
    -                                tuple(self.dbapi.version.split(".")) < (2, 1, 8)
    +                            self._dbapi_version() < (2, 1, 8)
    
     dialect = MSDialect_pyodbc
    
  2. Log in to comment