pymssql.py 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. # mssql/pymssql.py
  2. # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. """
  8. .. dialect:: mssql+pymssql
  9. :name: pymssql
  10. :dbapi: pymssql
  11. :connectstring: mssql+pymssql://<username>:<password>@<freetds_name>/?\
  12. charset=utf8
  13. :url: http://pymssql.org/
  14. pymssql is a Python module that provides a Python DBAPI interface around
  15. `FreeTDS <http://www.freetds.org/>`_. Compatible builds are available for
  16. Linux, MacOSX and Windows platforms.
  17. """
  18. from .base import MSDialect
  19. from ... import types as sqltypes, util, processors
  20. import re
  21. class _MSNumeric_pymssql(sqltypes.Numeric):
  22. def result_processor(self, dialect, type_):
  23. if not self.asdecimal:
  24. return processors.to_float
  25. else:
  26. return sqltypes.Numeric.result_processor(self, dialect, type_)
  27. class MSDialect_pymssql(MSDialect):
  28. supports_sane_rowcount = False
  29. driver = 'pymssql'
  30. colspecs = util.update_copy(
  31. MSDialect.colspecs,
  32. {
  33. sqltypes.Numeric: _MSNumeric_pymssql,
  34. sqltypes.Float: sqltypes.Float,
  35. }
  36. )
  37. @classmethod
  38. def dbapi(cls):
  39. module = __import__('pymssql')
  40. # pymmsql < 2.1.1 doesn't have a Binary method. we use string
  41. client_ver = tuple(int(x) for x in module.__version__.split("."))
  42. if client_ver < (2, 1, 1):
  43. # TODO: monkeypatching here is less than ideal
  44. module.Binary = lambda x: x if hasattr(x, 'decode') else str(x)
  45. if client_ver < (1, ):
  46. util.warn("The pymssql dialect expects at least "
  47. "the 1.0 series of the pymssql DBAPI.")
  48. return module
  49. def __init__(self, **params):
  50. super(MSDialect_pymssql, self).__init__(**params)
  51. self.use_scope_identity = True
  52. def _get_server_version_info(self, connection):
  53. vers = connection.scalar("select @@version")
  54. m = re.match(
  55. r"Microsoft .*? - (\d+).(\d+).(\d+).(\d+)", vers)
  56. if m:
  57. return tuple(int(x) for x in m.group(1, 2, 3, 4))
  58. else:
  59. return None
  60. def create_connect_args(self, url):
  61. opts = url.translate_connect_args(username='user')
  62. opts.update(url.query)
  63. port = opts.pop('port', None)
  64. if port and 'host' in opts:
  65. opts['host'] = "%s:%s" % (opts['host'], port)
  66. return [[], opts]
  67. def is_disconnect(self, e, connection, cursor):
  68. for msg in (
  69. "Adaptive Server connection timed out",
  70. "Net-Lib error during Connection reset by peer",
  71. "message 20003", # connection timeout
  72. "Error 10054",
  73. "Not connected to any MS SQL server",
  74. "Connection is closed",
  75. "message 20006", # Write to the server failed
  76. "message 20017", # Unexpected EOF from the server
  77. ):
  78. if msg in str(e):
  79. return True
  80. else:
  81. return False
  82. dialect = MSDialect_pymssql