Skip to content

Commit 5717bcc

Browse files
committed
_binary prefix is now optional
1 parent 0408bf4 commit 5717bcc

File tree

2 files changed

+43
-32
lines changed

2 files changed

+43
-32
lines changed

pymysql/connections.py

+33-29
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818

1919
from .charset import MBLENGTH, charset_by_name, charset_by_id
2020
from .constants import CLIENT, COMMAND, CR, FIELD_TYPE, SERVER_STATUS
21-
from .converters import escape_item, escape_string, through, conversions as _conv
21+
from . import converters
22+
from .converters import conversions as _conv
2223
from .cursors import Cursor
2324
from .optionfile import Parser
2425
from .util import byte2int, int2byte
@@ -44,39 +45,28 @@
4445

4546
_py_version = sys.version_info[:2]
4647

48+
if PY2:
49+
pass
50+
elif _py_version < (3, 6):
51+
# See http://bugs.python.org/issue24870
52+
_surrogateescape_table = [chr(i) if i < 0x80 else chr(i + 0xdc00) for i in range(256)]
53+
54+
def _fast_surrogateescape(s):
55+
return s.decode('latin1').translate(_surrogateescape_table)
56+
else:
57+
def _fast_surrogateescape(s):
58+
return s.decode('ascii', 'surrogateescape')
4759

4860
# socket.makefile() in Python 2 is not usable because very inefficient and
4961
# bad behavior about timeout.
5062
# XXX: ._socketio doesn't work under IronPython.
51-
if _py_version == (2, 7) and not IRONPYTHON:
63+
if PY2 and not IRONPYTHON:
5264
# read method of file-like returned by sock.makefile() is very slow.
5365
# So we copy io-based one from Python 3.
5466
from ._socketio import SocketIO
5567

5668
def _makefile(sock, mode):
5769
return io.BufferedReader(SocketIO(sock, mode))
58-
elif _py_version == (2, 6):
59-
# Python 2.6 doesn't have fast io module.
60-
# So we make original one.
61-
class SockFile(object):
62-
def __init__(self, sock):
63-
self._sock = sock
64-
65-
def read(self, n):
66-
read = self._sock.recv(n)
67-
if len(read) == n:
68-
return read
69-
while True:
70-
data = self._sock.recv(n-len(read))
71-
if not data:
72-
return read
73-
read += data
74-
if len(read) == n:
75-
return read
76-
77-
def _makefile(sock, mode):
78-
assert mode == 'rb'
79-
return SockFile(sock)
8070
else:
8171
# socket.makefile in Python 3 is nice.
8272
def _makefile(sock, mode):
@@ -570,6 +560,7 @@ class Connection(object):
570560
(if no authenticate method) for returning a string from the user. (experimental)
571561
:param db: Alias for database. (for compatibility to MySQLdb)
572562
:param passwd: Alias for password. (for compatibility to MySQLdb)
563+
:param binary_prefix: Add _binary prefix on bytes and bytearray. (default: False)
573564
"""
574565

575566
_sock = None
@@ -586,7 +577,7 @@ def __init__(self, host=None, user=None, password="",
586577
autocommit=False, db=None, passwd=None, local_infile=False,
587578
max_allowed_packet=16*1024*1024, defer_connect=False,
588579
auth_plugin_map={}, read_timeout=None, write_timeout=None,
589-
bind_address=None):
580+
bind_address=None, binary_prefix=False):
590581
if no_delay is not None:
591582
warnings.warn("no_delay option is deprecated", DeprecationWarning)
592583

@@ -693,14 +684,16 @@ def _config(key, arg):
693684
self.autocommit_mode = autocommit
694685

695686
if conv is None:
696-
conv = _conv
687+
conv = converters.conversions
688+
697689
# Need for MySQLdb compatibility.
698690
self.encoders = dict([(k, v) for (k, v) in conv.items() if type(k) is not int])
699691
self.decoders = dict([(k, v) for (k, v) in conv.items() if type(k) is int])
700692
self.sql_mode = sql_mode
701693
self.init_command = init_command
702694
self.max_allowed_packet = max_allowed_packet
703695
self._auth_plugin_map = auth_plugin_map
696+
self._binary_prefix = binary_prefix
704697
if defer_connect:
705698
self._sock = None
706699
else:
@@ -812,7 +805,12 @@ def escape(self, obj, mapping=None):
812805
"""
813806
if isinstance(obj, str_type):
814807
return "'" + self.escape_string(obj) + "'"
815-
return escape_item(obj, self.charset, mapping=mapping)
808+
if isinstance(obj, (bytes, bytearray)):
809+
ret = self._quote_bytes(obj)
810+
if self._binary_prefix:
811+
ret = "_binary" + ret
812+
return ret
813+
return converters.escape_item(obj, self.charset, mapping=mapping)
816814

817815
def literal(self, obj):
818816
"""Alias for escape()
@@ -825,7 +823,13 @@ def escape_string(self, s):
825823
if (self.server_status &
826824
SERVER_STATUS.SERVER_STATUS_NO_BACKSLASH_ESCAPES):
827825
return s.replace("'", "''")
828-
return escape_string(s)
826+
return converters.escape_string(s)
827+
828+
def _quote_bytes(self, s):
829+
if (self.server_status &
830+
SERVER_STATUS.SERVER_STATUS_NO_BACKSLASH_ESCAPES):
831+
return "'%s'" % (_fast_surrogateescape(s.replace(b"'", b"''")),)
832+
return converters.escape_bytes(s)
829833

830834
def cursor(self, cursor=None):
831835
"""Create a new cursor to execute queries with"""
@@ -1510,7 +1514,7 @@ def _get_descriptions(self):
15101514
else:
15111515
encoding = None
15121516
converter = self.connection.decoders.get(field_type)
1513-
if converter is through:
1517+
if converter is converters.through:
15141518
converter = None
15151519
if DEBUG: print("DEBUG: field={}, converter={}".format(field, converter))
15161520
self.converters.append((encoding, converter))

pymysql/converters.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,14 @@ def escape_string(value, mapping=None):
9090
value = value.replace('"', '\\"')
9191
return value
9292

93-
def escape_bytes(value, mapping=None):
93+
def escape_bytes_prefixed(value, mapping=None):
9494
assert isinstance(value, (bytes, bytearray))
9595
return b"_binary'%s'" % escape_string(value)
96+
97+
def escape_bytes(value, mapping=None):
98+
assert isinstance(value, (bytes, bytearray))
99+
return b"'%s'" % escape_string(value)
100+
96101
else:
97102
escape_string = _escape_unicode
98103

@@ -102,9 +107,12 @@ def escape_bytes(value, mapping=None):
102107
# We can escape special chars and surrogateescape at once.
103108
_escape_bytes_table = _escape_table + [chr(i) for i in range(0xdc80, 0xdd00)]
104109

105-
def escape_bytes(value, mapping=None):
110+
def escape_bytes_prefixed(value, mapping=None):
106111
return "_binary'%s'" % value.decode('latin1').translate(_escape_bytes_table)
107112

113+
def escape_bytes(value, mapping=None):
114+
return "'%s'" % value.decode('latin1').translate(_escape_bytes_table)
115+
108116

109117
def escape_unicode(value, mapping=None):
110118
return u"'%s'" % _escape_unicode(value)
@@ -373,7 +381,6 @@ def convert_characters(connection, field, data):
373381
set: escape_sequence,
374382
frozenset: escape_sequence,
375383
dict: escape_dict,
376-
bytearray: escape_bytes,
377384
type(None): escape_None,
378385
datetime.date: escape_date,
379386
datetime.datetime: escape_datetime,

0 commit comments

Comments
 (0)