oursql.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. # mysql/oursql.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:: mysql+oursql
  9. :name: OurSQL
  10. :dbapi: oursql
  11. :connectstring: mysql+oursql://<user>:<password>@<host>[:<port>]/<dbname>
  12. :url: http://packages.python.org/oursql/
  13. Unicode
  14. -------
  15. Please see :ref:`mysql_unicode` for current recommendations on unicode
  16. handling.
  17. """
  18. import re
  19. from .base import (BIT, MySQLDialect, MySQLExecutionContext)
  20. from ... import types as sqltypes, util
  21. class _oursqlBIT(BIT):
  22. def result_processor(self, dialect, coltype):
  23. """oursql already converts mysql bits, so."""
  24. return None
  25. class MySQLExecutionContext_oursql(MySQLExecutionContext):
  26. @property
  27. def plain_query(self):
  28. return self.execution_options.get('_oursql_plain_query', False)
  29. class MySQLDialect_oursql(MySQLDialect):
  30. driver = 'oursql'
  31. if util.py2k:
  32. supports_unicode_binds = True
  33. supports_unicode_statements = True
  34. supports_native_decimal = True
  35. supports_sane_rowcount = True
  36. supports_sane_multi_rowcount = True
  37. execution_ctx_cls = MySQLExecutionContext_oursql
  38. colspecs = util.update_copy(
  39. MySQLDialect.colspecs,
  40. {
  41. sqltypes.Time: sqltypes.Time,
  42. BIT: _oursqlBIT,
  43. }
  44. )
  45. @classmethod
  46. def dbapi(cls):
  47. return __import__('oursql')
  48. def do_execute(self, cursor, statement, parameters, context=None):
  49. """Provide an implementation of
  50. *cursor.execute(statement, parameters)*."""
  51. if context and context.plain_query:
  52. cursor.execute(statement, plain_query=True)
  53. else:
  54. cursor.execute(statement, parameters)
  55. def do_begin(self, connection):
  56. connection.cursor().execute('BEGIN', plain_query=True)
  57. def _xa_query(self, connection, query, xid):
  58. if util.py2k:
  59. arg = connection.connection._escape_string(xid)
  60. else:
  61. charset = self._connection_charset
  62. arg = connection.connection._escape_string(
  63. xid.encode(charset)).decode(charset)
  64. arg = "'%s'" % arg
  65. connection.execution_options(
  66. _oursql_plain_query=True).execute(query % arg)
  67. # Because mysql is bad, these methods have to be
  68. # reimplemented to use _PlainQuery. Basically, some queries
  69. # refuse to return any data if they're run through
  70. # the parameterized query API, or refuse to be parameterized
  71. # in the first place.
  72. def do_begin_twophase(self, connection, xid):
  73. self._xa_query(connection, 'XA BEGIN %s', xid)
  74. def do_prepare_twophase(self, connection, xid):
  75. self._xa_query(connection, 'XA END %s', xid)
  76. self._xa_query(connection, 'XA PREPARE %s', xid)
  77. def do_rollback_twophase(self, connection, xid, is_prepared=True,
  78. recover=False):
  79. if not is_prepared:
  80. self._xa_query(connection, 'XA END %s', xid)
  81. self._xa_query(connection, 'XA ROLLBACK %s', xid)
  82. def do_commit_twophase(self, connection, xid, is_prepared=True,
  83. recover=False):
  84. if not is_prepared:
  85. self.do_prepare_twophase(connection, xid)
  86. self._xa_query(connection, 'XA COMMIT %s', xid)
  87. # Q: why didn't we need all these "plain_query" overrides earlier ?
  88. # am i on a newer/older version of OurSQL ?
  89. def has_table(self, connection, table_name, schema=None):
  90. return MySQLDialect.has_table(
  91. self,
  92. connection.connect().execution_options(_oursql_plain_query=True),
  93. table_name,
  94. schema
  95. )
  96. def get_table_options(self, connection, table_name, schema=None, **kw):
  97. return MySQLDialect.get_table_options(
  98. self,
  99. connection.connect().execution_options(_oursql_plain_query=True),
  100. table_name,
  101. schema=schema,
  102. **kw
  103. )
  104. def get_columns(self, connection, table_name, schema=None, **kw):
  105. return MySQLDialect.get_columns(
  106. self,
  107. connection.connect().execution_options(_oursql_plain_query=True),
  108. table_name,
  109. schema=schema,
  110. **kw
  111. )
  112. def get_view_names(self, connection, schema=None, **kw):
  113. return MySQLDialect.get_view_names(
  114. self,
  115. connection.connect().execution_options(_oursql_plain_query=True),
  116. schema=schema,
  117. **kw
  118. )
  119. def get_table_names(self, connection, schema=None, **kw):
  120. return MySQLDialect.get_table_names(
  121. self,
  122. connection.connect().execution_options(_oursql_plain_query=True),
  123. schema
  124. )
  125. def get_schema_names(self, connection, **kw):
  126. return MySQLDialect.get_schema_names(
  127. self,
  128. connection.connect().execution_options(_oursql_plain_query=True),
  129. **kw
  130. )
  131. def initialize(self, connection):
  132. return MySQLDialect.initialize(
  133. self,
  134. connection.execution_options(_oursql_plain_query=True)
  135. )
  136. def _show_create_table(self, connection, table, charset=None,
  137. full_name=None):
  138. return MySQLDialect._show_create_table(
  139. self,
  140. connection.contextual_connect(close_with_result=True).
  141. execution_options(_oursql_plain_query=True),
  142. table, charset, full_name
  143. )
  144. def is_disconnect(self, e, connection, cursor):
  145. if isinstance(e, self.dbapi.ProgrammingError):
  146. return e.errno is None and 'cursor' not in e.args[1] \
  147. and e.args[1].endswith('closed')
  148. else:
  149. return e.errno in (2006, 2013, 2014, 2045, 2055)
  150. def create_connect_args(self, url):
  151. opts = url.translate_connect_args(database='db', username='user',
  152. password='passwd')
  153. opts.update(url.query)
  154. util.coerce_kw_type(opts, 'port', int)
  155. util.coerce_kw_type(opts, 'compress', bool)
  156. util.coerce_kw_type(opts, 'autoping', bool)
  157. util.coerce_kw_type(opts, 'raise_on_warnings', bool)
  158. util.coerce_kw_type(opts, 'default_charset', bool)
  159. if opts.pop('default_charset', False):
  160. opts['charset'] = None
  161. else:
  162. util.coerce_kw_type(opts, 'charset', str)
  163. opts['use_unicode'] = opts.get('use_unicode', True)
  164. util.coerce_kw_type(opts, 'use_unicode', bool)
  165. # FOUND_ROWS must be set in CLIENT_FLAGS to enable
  166. # supports_sane_rowcount.
  167. opts.setdefault('found_rows', True)
  168. ssl = {}
  169. for key in ['ssl_ca', 'ssl_key', 'ssl_cert',
  170. 'ssl_capath', 'ssl_cipher']:
  171. if key in opts:
  172. ssl[key[4:]] = opts[key]
  173. util.coerce_kw_type(ssl, key[4:], str)
  174. del opts[key]
  175. if ssl:
  176. opts['ssl'] = ssl
  177. return [[], opts]
  178. def _get_server_version_info(self, connection):
  179. dbapi_con = connection.connection
  180. version = []
  181. r = re.compile(r'[.\-]')
  182. for n in r.split(dbapi_con.server_info):
  183. try:
  184. version.append(int(n))
  185. except ValueError:
  186. version.append(n)
  187. return tuple(version)
  188. def _extract_error_code(self, exception):
  189. return exception.errno
  190. def _detect_charset(self, connection):
  191. """Sniff out the character set in use for connection results."""
  192. return connection.connection.charset
  193. def _compat_fetchall(self, rp, charset=None):
  194. """oursql isn't super-broken like MySQLdb, yaaay."""
  195. return rp.fetchall()
  196. def _compat_fetchone(self, rp, charset=None):
  197. """oursql isn't super-broken like MySQLdb, yaaay."""
  198. return rp.fetchone()
  199. def _compat_first(self, rp, charset=None):
  200. return rp.first()
  201. dialect = MySQLDialect_oursql