Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# mysql/oursql.py 

2# Copyright (C) 2005-2020 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""" 

9 

10.. dialect:: mysql+oursql 

11 :name: OurSQL 

12 :dbapi: oursql 

13 :connectstring: mysql+oursql://<user>:<password>@<host>[:<port>]/<dbname> 

14 :url: http://packages.python.org/oursql/ 

15 

16.. note:: 

17 

18 The OurSQL MySQL dialect is legacy and is no longer supported upstream, 

19 and is **not tested as part of SQLAlchemy's continuous integration**. 

20 The recommended MySQL dialects are mysqlclient and PyMySQL. 

21 

22Unicode 

23------- 

24 

25Please see :ref:`mysql_unicode` for current recommendations on unicode 

26handling. 

27 

28 

29""" 

30 

31 

32from .base import BIT 

33from .base import MySQLDialect 

34from .base import MySQLExecutionContext 

35from ... import types as sqltypes 

36from ... import util 

37 

38 

39class _oursqlBIT(BIT): 

40 def result_processor(self, dialect, coltype): 

41 """oursql already converts mysql bits, so.""" 

42 

43 return None 

44 

45 

46class MySQLExecutionContext_oursql(MySQLExecutionContext): 

47 @property 

48 def plain_query(self): 

49 return self.execution_options.get("_oursql_plain_query", False) 

50 

51 

52class MySQLDialect_oursql(MySQLDialect): 

53 driver = "oursql" 

54 

55 if util.py2k: 

56 supports_unicode_binds = True 

57 supports_unicode_statements = True 

58 

59 supports_native_decimal = True 

60 

61 supports_sane_rowcount = True 

62 supports_sane_multi_rowcount = True 

63 execution_ctx_cls = MySQLExecutionContext_oursql 

64 

65 colspecs = util.update_copy( 

66 MySQLDialect.colspecs, {sqltypes.Time: sqltypes.Time, BIT: _oursqlBIT} 

67 ) 

68 

69 @classmethod 

70 def dbapi(cls): 

71 return __import__("oursql") 

72 

73 def do_execute(self, cursor, statement, parameters, context=None): 

74 """Provide an implementation of 

75 *cursor.execute(statement, parameters)*.""" 

76 

77 if context and context.plain_query: 

78 cursor.execute(statement, plain_query=True) 

79 else: 

80 cursor.execute(statement, parameters) 

81 

82 def do_begin(self, connection): 

83 connection.cursor().execute("BEGIN", plain_query=True) 

84 

85 def _xa_query(self, connection, query, xid): 

86 if util.py2k: 

87 arg = connection.connection._escape_string(xid) 

88 else: 

89 charset = self._connection_charset 

90 arg = connection.connection._escape_string( 

91 xid.encode(charset) 

92 ).decode(charset) 

93 arg = "'%s'" % arg 

94 connection.execution_options(_oursql_plain_query=True).execute( 

95 query % arg 

96 ) 

97 

98 # Because mysql is bad, these methods have to be 

99 # reimplemented to use _PlainQuery. Basically, some queries 

100 # refuse to return any data if they're run through 

101 # the parameterized query API, or refuse to be parameterized 

102 # in the first place. 

103 def do_begin_twophase(self, connection, xid): 

104 self._xa_query(connection, "XA BEGIN %s", xid) 

105 

106 def do_prepare_twophase(self, connection, xid): 

107 self._xa_query(connection, "XA END %s", xid) 

108 self._xa_query(connection, "XA PREPARE %s", xid) 

109 

110 def do_rollback_twophase( 

111 self, connection, xid, is_prepared=True, recover=False 

112 ): 

113 if not is_prepared: 

114 self._xa_query(connection, "XA END %s", xid) 

115 self._xa_query(connection, "XA ROLLBACK %s", xid) 

116 

117 def do_commit_twophase( 

118 self, connection, xid, is_prepared=True, recover=False 

119 ): 

120 if not is_prepared: 

121 self.do_prepare_twophase(connection, xid) 

122 self._xa_query(connection, "XA COMMIT %s", xid) 

123 

124 # Q: why didn't we need all these "plain_query" overrides earlier ? 

125 # am i on a newer/older version of OurSQL ? 

126 def has_table(self, connection, table_name, schema=None): 

127 return MySQLDialect.has_table( 

128 self, 

129 connection.connect().execution_options(_oursql_plain_query=True), 

130 table_name, 

131 schema, 

132 ) 

133 

134 def get_table_options(self, connection, table_name, schema=None, **kw): 

135 return MySQLDialect.get_table_options( 

136 self, 

137 connection.connect().execution_options(_oursql_plain_query=True), 

138 table_name, 

139 schema=schema, 

140 **kw 

141 ) 

142 

143 def get_columns(self, connection, table_name, schema=None, **kw): 

144 return MySQLDialect.get_columns( 

145 self, 

146 connection.connect().execution_options(_oursql_plain_query=True), 

147 table_name, 

148 schema=schema, 

149 **kw 

150 ) 

151 

152 def get_view_names(self, connection, schema=None, **kw): 

153 return MySQLDialect.get_view_names( 

154 self, 

155 connection.connect().execution_options(_oursql_plain_query=True), 

156 schema=schema, 

157 **kw 

158 ) 

159 

160 def get_table_names(self, connection, schema=None, **kw): 

161 return MySQLDialect.get_table_names( 

162 self, 

163 connection.connect().execution_options(_oursql_plain_query=True), 

164 schema, 

165 ) 

166 

167 def get_schema_names(self, connection, **kw): 

168 return MySQLDialect.get_schema_names( 

169 self, 

170 connection.connect().execution_options(_oursql_plain_query=True), 

171 **kw 

172 ) 

173 

174 def initialize(self, connection): 

175 return MySQLDialect.initialize( 

176 self, connection.execution_options(_oursql_plain_query=True) 

177 ) 

178 

179 def _show_create_table( 

180 self, connection, table, charset=None, full_name=None 

181 ): 

182 return MySQLDialect._show_create_table( 

183 self, 

184 connection._contextual_connect( 

185 close_with_result=True 

186 ).execution_options(_oursql_plain_query=True), 

187 table, 

188 charset, 

189 full_name, 

190 ) 

191 

192 def is_disconnect(self, e, connection, cursor): 

193 if isinstance(e, self.dbapi.ProgrammingError): 

194 return ( 

195 e.errno is None 

196 and "cursor" not in e.args[1] 

197 and e.args[1].endswith("closed") 

198 ) 

199 else: 

200 return e.errno in (2006, 2013, 2014, 2045, 2055) 

201 

202 def create_connect_args(self, url): 

203 opts = url.translate_connect_args( 

204 database="db", username="user", password="passwd" 

205 ) 

206 opts.update(url.query) 

207 

208 util.coerce_kw_type(opts, "port", int) 

209 util.coerce_kw_type(opts, "compress", bool) 

210 util.coerce_kw_type(opts, "autoping", bool) 

211 util.coerce_kw_type(opts, "raise_on_warnings", bool) 

212 

213 util.coerce_kw_type(opts, "default_charset", bool) 

214 if opts.pop("default_charset", False): 

215 opts["charset"] = None 

216 else: 

217 util.coerce_kw_type(opts, "charset", str) 

218 opts["use_unicode"] = opts.get("use_unicode", True) 

219 util.coerce_kw_type(opts, "use_unicode", bool) 

220 

221 # FOUND_ROWS must be set in CLIENT_FLAGS to enable 

222 # supports_sane_rowcount. 

223 opts.setdefault("found_rows", True) 

224 

225 ssl = {} 

226 for key in [ 

227 "ssl_ca", 

228 "ssl_key", 

229 "ssl_cert", 

230 "ssl_capath", 

231 "ssl_cipher", 

232 ]: 

233 if key in opts: 

234 ssl[key[4:]] = opts[key] 

235 util.coerce_kw_type(ssl, key[4:], str) 

236 del opts[key] 

237 if ssl: 

238 opts["ssl"] = ssl 

239 

240 return [[], opts] 

241 

242 def _extract_error_code(self, exception): 

243 return exception.errno 

244 

245 def _detect_charset(self, connection): 

246 """Sniff out the character set in use for connection results.""" 

247 

248 return connection.connection.charset 

249 

250 def _compat_fetchall(self, rp, charset=None): 

251 """oursql isn't super-broken like MySQLdb, yaaay.""" 

252 return rp.fetchall() 

253 

254 def _compat_fetchone(self, rp, charset=None): 

255 """oursql isn't super-broken like MySQLdb, yaaay.""" 

256 return rp.fetchone() 

257 

258 def _compat_first(self, rp, charset=None): 

259 return rp.first() 

260 

261 

262dialect = MySQLDialect_oursql