faster surrogateescape

This commit is contained in:
INADA Naoki
2016-01-12 01:08:26 +09:00
parent 5d91470b0f
commit 6ff138a597
2 changed files with 26 additions and 24 deletions

View File

@ -1,10 +1,8 @@
""" """
This module implements connections for MySQLdb. Presently there is This module implements connections for MySQLdb. Presently there is
only one class: Connection. Others are unlikely. However, you might only one class: Connection. Others are unlikely. However, you might
want to make your own subclasses. In most cases, you will probably want to make your own subclasses. In most cases, you will probably
override Connection.default_cursor with a non-standard Cursor class. override Connection.default_cursor with a non-standard Cursor class.
""" """
from MySQLdb import cursors from MySQLdb import cursors
from MySQLdb.compat import unicode, PY2 from MySQLdb.compat import unicode, PY2
@ -15,6 +13,14 @@ import _mysql
import re import re
if not PY2:
# See http://bugs.python.org/issue24870
_surrogateescape_table = [chr(i) if i < 0x80 else chr(i + 0xdc00) for i in range(256)]
def _fast_surroundescape(s):
return s.decode('latin1').translate(_surrogateescape_table)
def defaulterrorhandler(connection, cursor, errorclass, errorvalue): def defaulterrorhandler(connection, cursor, errorclass, errorvalue):
""" """
If cursor is not None, (errorclass, errorvalue) is appended to If cursor is not None, (errorclass, errorvalue) is appended to
@ -34,7 +40,7 @@ def defaulterrorhandler(connection, cursor, errorclass, errorvalue):
del connection del connection
if isinstance(errorvalue, BaseException): if isinstance(errorvalue, BaseException):
raise errorvalue raise errorvalue
if errorclass is not None: if errorclass is not None:
raise errorclass(errorvalue) raise errorclass(errorvalue)
else: else:
raise Exception(errorvalue) raise Exception(errorvalue)
@ -291,24 +297,21 @@ class Connection(_mysql.connection):
self.commit() self.commit()
def literal(self, o): def literal(self, o):
""" """If o is a single object, returns an SQL literal as a string.
If o is a single object, returns an SQL literal as a string.
If o is a non-string sequence, the items of the sequence are If o is a non-string sequence, the items of the sequence are
converted and returned as a sequence. converted and returned as a sequence.
Non-standard. For internal use; do not use this in your Non-standard. For internal use; do not use this in your
applications. applications.
""" """
s = self.escape(o, self.encoders) s = self.escape(o, self.encoders)
# Python 3 doesn't support % operation for bytes object. # Python 3(~3.4) doesn't support % operation for bytes object.
# We should decode it before using %. # We should decode it before using %.
# Decoding with ascii and surrogateescape allows convert arbitrary # Decoding with ascii and surrogateescape allows convert arbitrary
# bytes to unicode and back again. # bytes to unicode and back again.
# See http://python.org/dev/peps/pep-0383/ # See http://python.org/dev/peps/pep-0383/
if not PY2 and isinstance(s, bytes): if not PY2 and isinstance(s, (bytes, bytearray)):
return s.decode('ascii', 'surrogateescape') return _fast_surroundescape(s)
return s return s
def begin(self): def begin(self):

View File

@ -187,7 +187,7 @@ class BaseCursor(object):
parameter placeholder in the query. If a mapping is used, parameter placeholder in the query. If a mapping is used,
%(key)s must be used as the placeholder. %(key)s must be used as the placeholder.
Returns long integer rows affected, if any Returns integer represents rows affected, if any
""" """
while self.nextset(): while self.nextset():
pass pass
@ -208,9 +208,12 @@ class BaseCursor(object):
args = dict((key, db.literal(item)) for key, item in args.items()) args = dict((key, db.literal(item)) for key, item in args.items())
else: else:
args = tuple(map(db.literal, args)) args = tuple(map(db.literal, args))
if not PY2 and isinstance(query, bytes): if not PY2 and isinstance(query, (bytes, bytearray)):
query = query.decode(db.unicode_literal.charset) query = query.decode(db.unicode_literal.charset)
query = query % args try:
query = query % args
except TypeError as m:
self.errorhandler(self, ProgrammingError, str(m))
if isinstance(query, unicode): if isinstance(query, unicode):
query = query.encode(db.unicode_literal.charset, 'surrogateescape') query = query.encode(db.unicode_literal.charset, 'surrogateescape')
@ -218,17 +221,12 @@ class BaseCursor(object):
res = None res = None
try: try:
res = self._query(query) res = self._query(query)
except TypeError as m:
if m.args[0] in ("not enough arguments for format string",
"not all arguments converted"):
self.errorhandler(self, ProgrammingError, m.args[0])
else:
self.errorhandler(self, TypeError, m)
except Exception: except Exception:
exc, value = sys.exc_info()[:2] exc, value = sys.exc_info()[:2]
self.errorhandler(self, exc, value) self.errorhandler(self, exc, value)
self._executed = query self._executed = query
if not self._defer_warnings: self._warning_check() if not self._defer_warnings:
self._warning_check()
return res return res
def executemany(self, query, args): def executemany(self, query, args):
@ -369,13 +367,13 @@ class BaseCursor(object):
class CursorStoreResultMixIn(object): class CursorStoreResultMixIn(object):
"""This is a MixIn class which causes the entire result set to be """This is a MixIn class which causes the entire result set to be
stored on the client side, i.e. it uses mysql_store_result(). If the stored on the client side, i.e. it uses mysql_store_result(). If the
result set can be very large, consider adding a LIMIT clause to your result set can be very large, consider adding a LIMIT clause to your
query, or using CursorUseResultMixIn instead.""" query, or using CursorUseResultMixIn instead."""
def _get_result(self): return self._get_db().store_result() def _get_result(self):
return self._get_db().store_result()
def _query(self, q): def _query(self, q):
rowcount = self._do_query(q) rowcount = self._do_query(q)
@ -390,9 +388,10 @@ class CursorStoreResultMixIn(object):
"""Fetches a single row from the cursor. None indicates that """Fetches a single row from the cursor. None indicates that
no more rows are available.""" no more rows are available."""
self._check_executed() self._check_executed()
if self.rownumber >= len(self._rows): return None if self.rownumber >= len(self._rows):
return None
result = self._rows[self.rownumber] result = self._rows[self.rownumber]
self.rownumber = self.rownumber+1 self.rownumber = self.rownumber + 1
return result return result
def fetchmany(self, size=None): def fetchmany(self, size=None):