Skip to content

Commit 84dcba2

Browse files
authored
Merge pull request #855 from dbcli/sql-insert-bytes
Fix binary data representation in SQL formatters
2 parents ee5126f + e622311 commit 84dcba2

File tree

3 files changed

+38
-20
lines changed

3 files changed

+38
-20
lines changed

mycli/packages/tabular_output/sql_format.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
preprocessors = ()
1010

1111

12+
def escape_for_sql_statement(value):
13+
if isinstance(value, bytes):
14+
return f"X'{value.hex()}'"
15+
else:
16+
return formatter.mycli.sqlexecute.conn.escape(value)
17+
18+
1219
def adapter(data, headers, table_format=None, **kwargs):
1320
tables = extract_tables(formatter.query)
1421
if len(tables) > 0:
@@ -19,13 +26,13 @@ def adapter(data, headers, table_format=None, **kwargs):
1926
table_name = table[1]
2027
else:
2128
table_name = "`DUAL`"
22-
escape = formatter.mycli.sqlexecute.conn.escape
2329
if table_format == 'sql-insert':
2430
h = "`, `".join(headers)
2531
yield "INSERT INTO {} (`{}`) VALUES".format(table_name, h)
2632
prefix = " "
2733
for d in data:
28-
values = ", ".join(escape(v) for i, v in enumerate(d))
34+
values = ", ".join(escape_for_sql_statement(v)
35+
for i, v in enumerate(d))
2936
yield "{}({})".format(prefix, values)
3037
if prefix == " ":
3138
prefix = ", "
@@ -39,11 +46,12 @@ def adapter(data, headers, table_format=None, **kwargs):
3946
yield "UPDATE {} SET".format(table_name)
4047
prefix = " "
4148
for i, v in enumerate(d[keys:], keys):
42-
yield "{}`{}` = {}".format(prefix, headers[i], escape(v))
49+
yield "{}`{}` = {}".format(prefix, headers[i], escape_for_sql_statement(v))
4350
if prefix == " ":
4451
prefix = ", "
4552
f = "`{}` = {}"
46-
where = (f.format(headers[i], escape(d[i])) for i in range(keys))
53+
where = (f.format(headers[i], escape_for_sql_statement(
54+
d[i])) for i in range(keys))
4755
yield "WHERE {};".format(" AND ".join(where))
4856

4957

test/test_tabular_output.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,21 @@ def mycli():
2323
@dbtest
2424
def test_sql_output(mycli):
2525
"""Test the sql output adapter."""
26-
headers = ['letters', 'number', 'optional', 'float']
26+
headers = ['letters', 'number', 'optional', 'float', 'binary']
2727

2828
class FakeCursor(object):
2929
def __init__(self):
30-
self.data = [('abc', 1, None, 10.0), ('d', 456, '1', 0.5)]
31-
self.description = [(None, FIELD_TYPE.VARCHAR), (None, FIELD_TYPE.LONG),
32-
(None, FIELD_TYPE.LONG), (None, FIELD_TYPE.FLOAT)]
30+
self.data = [
31+
('abc', 1, None, 10.0, b'\xAA'),
32+
('d', 456, '1', 0.5, b'\xAA\xBB')
33+
]
34+
self.description = [
35+
(None, FIELD_TYPE.VARCHAR),
36+
(None, FIELD_TYPE.LONG),
37+
(None, FIELD_TYPE.LONG),
38+
(None, FIELD_TYPE.FLOAT),
39+
(None, FIELD_TYPE.BLOB)
40+
]
3341

3442
def __iter__(self):
3543
return self
@@ -40,8 +48,6 @@ def __next__(self):
4048
else:
4149
raise StopIteration()
4250

43-
next = __next__ # Python 2
44-
4551
def description(self):
4652
return self.description
4753

@@ -55,11 +61,13 @@ def description(self):
5561
`number` = 1
5662
, `optional` = NULL
5763
, `float` = 10
64+
, `binary` = X'aa'
5865
WHERE `letters` = 'abc';
5966
UPDATE `DUAL` SET
6067
`number` = 456
6168
, `optional` = '1'
6269
, `float` = 0.5
70+
, `binary` = X'aabb'
6371
WHERE `letters` = 'd';''')
6472
# Test sql-update-2 output format
6573
assert list(mycli.change_table_format("sql-update-2")) == \
@@ -70,38 +78,40 @@ def description(self):
7078
UPDATE `DUAL` SET
7179
`optional` = NULL
7280
, `float` = 10
81+
, `binary` = X'aa'
7382
WHERE `letters` = 'abc' AND `number` = 1;
7483
UPDATE `DUAL` SET
7584
`optional` = '1'
7685
, `float` = 0.5
86+
, `binary` = X'aabb'
7787
WHERE `letters` = 'd' AND `number` = 456;''')
7888
# Test sql-insert output format (without table name)
7989
assert list(mycli.change_table_format("sql-insert")) == \
8090
[(None, None, None, 'Changed table format to sql-insert')]
8191
mycli.formatter.query = ""
8292
output = mycli.format_output(None, FakeCursor(), headers)
8393
assert "\n".join(output) == dedent('''\
84-
INSERT INTO `DUAL` (`letters`, `number`, `optional`, `float`) VALUES
85-
('abc', 1, NULL, 10)
86-
, ('d', 456, '1', 0.5)
94+
INSERT INTO `DUAL` (`letters`, `number`, `optional`, `float`, `binary`) VALUES
95+
('abc', 1, NULL, 10, X'aa')
96+
, ('d', 456, '1', 0.5, X'aabb')
8797
;''')
8898
# Test sql-insert output format (with table name)
8999
assert list(mycli.change_table_format("sql-insert")) == \
90100
[(None, None, None, 'Changed table format to sql-insert')]
91101
mycli.formatter.query = "SELECT * FROM `table`"
92102
output = mycli.format_output(None, FakeCursor(), headers)
93103
assert "\n".join(output) == dedent('''\
94-
INSERT INTO `table` (`letters`, `number`, `optional`, `float`) VALUES
95-
('abc', 1, NULL, 10)
96-
, ('d', 456, '1', 0.5)
104+
INSERT INTO `table` (`letters`, `number`, `optional`, `float`, `binary`) VALUES
105+
('abc', 1, NULL, 10, X'aa')
106+
, ('d', 456, '1', 0.5, X'aabb')
97107
;''')
98108
# Test sql-insert output format (with database + table name)
99109
assert list(mycli.change_table_format("sql-insert")) == \
100110
[(None, None, None, 'Changed table format to sql-insert')]
101111
mycli.formatter.query = "SELECT * FROM `database`.`table`"
102112
output = mycli.format_output(None, FakeCursor(), headers)
103113
assert "\n".join(output) == dedent('''\
104-
INSERT INTO `database`.`table` (`letters`, `number`, `optional`, `float`) VALUES
105-
('abc', 1, NULL, 10)
106-
, ('d', 456, '1', 0.5)
114+
INSERT INTO `database`.`table` (`letters`, `number`, `optional`, `float`, `binary`) VALUES
115+
('abc', 1, NULL, 10, X'aa')
116+
, ('d', 456, '1', 0.5, X'aabb')
107117
;''')

test/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
PASSWORD = os.getenv('PYTEST_PASSWORD')
1313
USER = os.getenv('PYTEST_USER', 'root')
1414
HOST = os.getenv('PYTEST_HOST', 'localhost')
15-
PORT = os.getenv('PYTEST_PORT', 3306)
15+
PORT = int(os.getenv('PYTEST_PORT', 3306))
1616
CHARSET = os.getenv('PYTEST_CHARSET', 'utf8')
1717
SSH_USER = os.getenv('PYTEST_SSH_USER', None)
1818
SSH_HOST = os.getenv('PYTEST_SSH_HOST', None)

0 commit comments

Comments
 (0)