oursql.py 8.6 KB

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