diff --git a/MySQLdb/connections.py b/MySQLdb/connections.py index 7ac040b..f51106c 100644 --- a/MySQLdb/connections.py +++ b/MySQLdb/connections.py @@ -1,10 +1,8 @@ """ - This module implements connections for MySQLdb. Presently there is only one class: Connection. Others are unlikely. However, you might want to make your own subclasses. In most cases, you will probably override Connection.default_cursor with a non-standard Cursor class. - """ from MySQLdb import cursors from MySQLdb.compat import unicode, PY2 @@ -15,6 +13,14 @@ import _mysql 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): """ If cursor is not None, (errorclass, errorvalue) is appended to @@ -34,7 +40,7 @@ def defaulterrorhandler(connection, cursor, errorclass, errorvalue): del connection if isinstance(errorvalue, BaseException): raise errorvalue - if errorclass is not None: + if errorclass is not None: raise errorclass(errorvalue) else: raise Exception(errorvalue) @@ -291,24 +297,21 @@ class Connection(_mysql.connection): self.commit() 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 converted and returned as a sequence. Non-standard. For internal use; do not use this in your applications. - """ 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 %. # Decoding with ascii and surrogateescape allows convert arbitrary # bytes to unicode and back again. # See http://python.org/dev/peps/pep-0383/ - if not PY2 and isinstance(s, bytes): - return s.decode('ascii', 'surrogateescape') + if not PY2 and isinstance(s, (bytes, bytearray)): + return _fast_surroundescape(s) return s def begin(self): diff --git a/MySQLdb/cursors.py b/MySQLdb/cursors.py index cbb31b7..661ce35 100644 --- a/MySQLdb/cursors.py +++ b/MySQLdb/cursors.py @@ -187,7 +187,7 @@ class BaseCursor(object): parameter placeholder in the query. If a mapping is used, %(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(): pass @@ -208,9 +208,12 @@ class BaseCursor(object): args = dict((key, db.literal(item)) for key, item in args.items()) else: 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 % args + try: + query = query % args + except TypeError as m: + self.errorhandler(self, ProgrammingError, str(m)) if isinstance(query, unicode): query = query.encode(db.unicode_literal.charset, 'surrogateescape') @@ -218,17 +221,12 @@ class BaseCursor(object): res = None try: 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: exc, value = sys.exc_info()[:2] self.errorhandler(self, exc, value) self._executed = query - if not self._defer_warnings: self._warning_check() + if not self._defer_warnings: + self._warning_check() return res def executemany(self, query, args): @@ -369,13 +367,13 @@ class BaseCursor(object): class CursorStoreResultMixIn(object): - """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 result set can be very large, consider adding a LIMIT clause to your 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): rowcount = self._do_query(q) @@ -390,9 +388,10 @@ class CursorStoreResultMixIn(object): """Fetches a single row from the cursor. None indicates that no more rows are available.""" self._check_executed() - if self.rownumber >= len(self._rows): return None + if self.rownumber >= len(self._rows): + return None result = self._rows[self.rownumber] - self.rownumber = self.rownumber+1 + self.rownumber = self.rownumber + 1 return result def fetchmany(self, size=None):