Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions mycli/packages/tabular_output/sql_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
preprocessors = ()


def escape_for_sql_statement(value):
if isinstance(value, bytes):
return f"X'{value.hex()}'"
else:
return formatter.mycli.sqlexecute.conn.escape(value)


def adapter(data, headers, table_format=None, **kwargs):
tables = extract_tables(formatter.query)
if len(tables) > 0:
Expand All @@ -19,13 +26,13 @@ def adapter(data, headers, table_format=None, **kwargs):
table_name = table[1]
else:
table_name = "`DUAL`"
escape = formatter.mycli.sqlexecute.conn.escape
if table_format == 'sql-insert':
h = "`, `".join(headers)
yield "INSERT INTO {} (`{}`) VALUES".format(table_name, h)
prefix = " "
for d in data:
values = ", ".join(escape(v) for i, v in enumerate(d))
values = ", ".join(escape_for_sql_statement(v)
for i, v in enumerate(d))
yield "{}({})".format(prefix, values)
if prefix == " ":
prefix = ", "
Expand All @@ -39,11 +46,12 @@ def adapter(data, headers, table_format=None, **kwargs):
yield "UPDATE {} SET".format(table_name)
prefix = " "
for i, v in enumerate(d[keys:], keys):
yield "{}`{}` = {}".format(prefix, headers[i], escape(v))
yield "{}`{}` = {}".format(prefix, headers[i], escape_for_sql_statement(v))
if prefix == " ":
prefix = ", "
f = "`{}` = {}"
where = (f.format(headers[i], escape(d[i])) for i in range(keys))
where = (f.format(headers[i], escape_for_sql_statement(
d[i])) for i in range(keys))
yield "WHERE {};".format(" AND ".join(where))


Expand Down
40 changes: 25 additions & 15 deletions test/test_tabular_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ def mycli():
@dbtest
def test_sql_output(mycli):
"""Test the sql output adapter."""
headers = ['letters', 'number', 'optional', 'float']
headers = ['letters', 'number', 'optional', 'float', 'binary']

class FakeCursor(object):
def __init__(self):
self.data = [('abc', 1, None, 10.0), ('d', 456, '1', 0.5)]
self.description = [(None, FIELD_TYPE.VARCHAR), (None, FIELD_TYPE.LONG),
(None, FIELD_TYPE.LONG), (None, FIELD_TYPE.FLOAT)]
self.data = [
('abc', 1, None, 10.0, b'\xAA'),
('d', 456, '1', 0.5, b'\xAA\xBB')
]
self.description = [
(None, FIELD_TYPE.VARCHAR),
(None, FIELD_TYPE.LONG),
(None, FIELD_TYPE.LONG),
(None, FIELD_TYPE.FLOAT),
(None, FIELD_TYPE.BLOB)
]

def __iter__(self):
return self
Expand All @@ -40,8 +48,6 @@ def __next__(self):
else:
raise StopIteration()

next = __next__ # Python 2

def description(self):
return self.description

Expand All @@ -55,11 +61,13 @@ def description(self):
`number` = 1
, `optional` = NULL
, `float` = 10
, `binary` = X'aa'
WHERE `letters` = 'abc';
UPDATE `DUAL` SET
`number` = 456
, `optional` = '1'
, `float` = 0.5
, `binary` = X'aabb'
WHERE `letters` = 'd';''')
# Test sql-update-2 output format
assert list(mycli.change_table_format("sql-update-2")) == \
Expand All @@ -70,38 +78,40 @@ def description(self):
UPDATE `DUAL` SET
`optional` = NULL
, `float` = 10
, `binary` = X'aa'
WHERE `letters` = 'abc' AND `number` = 1;
UPDATE `DUAL` SET
`optional` = '1'
, `float` = 0.5
, `binary` = X'aabb'
WHERE `letters` = 'd' AND `number` = 456;''')
# Test sql-insert output format (without table name)
assert list(mycli.change_table_format("sql-insert")) == \
[(None, None, None, 'Changed table format to sql-insert')]
mycli.formatter.query = ""
output = mycli.format_output(None, FakeCursor(), headers)
assert "\n".join(output) == dedent('''\
INSERT INTO `DUAL` (`letters`, `number`, `optional`, `float`) VALUES
('abc', 1, NULL, 10)
, ('d', 456, '1', 0.5)
INSERT INTO `DUAL` (`letters`, `number`, `optional`, `float`, `binary`) VALUES
('abc', 1, NULL, 10, X'aa')
, ('d', 456, '1', 0.5, X'aabb')
;''')
# Test sql-insert output format (with table name)
assert list(mycli.change_table_format("sql-insert")) == \
[(None, None, None, 'Changed table format to sql-insert')]
mycli.formatter.query = "SELECT * FROM `table`"
output = mycli.format_output(None, FakeCursor(), headers)
assert "\n".join(output) == dedent('''\
INSERT INTO `table` (`letters`, `number`, `optional`, `float`) VALUES
('abc', 1, NULL, 10)
, ('d', 456, '1', 0.5)
INSERT INTO `table` (`letters`, `number`, `optional`, `float`, `binary`) VALUES
('abc', 1, NULL, 10, X'aa')
, ('d', 456, '1', 0.5, X'aabb')
;''')
# Test sql-insert output format (with database + table name)
assert list(mycli.change_table_format("sql-insert")) == \
[(None, None, None, 'Changed table format to sql-insert')]
mycli.formatter.query = "SELECT * FROM `database`.`table`"
output = mycli.format_output(None, FakeCursor(), headers)
assert "\n".join(output) == dedent('''\
INSERT INTO `database`.`table` (`letters`, `number`, `optional`, `float`) VALUES
('abc', 1, NULL, 10)
, ('d', 456, '1', 0.5)
INSERT INTO `database`.`table` (`letters`, `number`, `optional`, `float`, `binary`) VALUES
('abc', 1, NULL, 10, X'aa')
, ('d', 456, '1', 0.5, X'aabb')
;''')
2 changes: 1 addition & 1 deletion test/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
PASSWORD = os.getenv('PYTEST_PASSWORD')
USER = os.getenv('PYTEST_USER', 'root')
HOST = os.getenv('PYTEST_HOST', 'localhost')
PORT = os.getenv('PYTEST_PORT', 3306)
PORT = int(os.getenv('PYTEST_PORT', 3306))
CHARSET = os.getenv('PYTEST_CHARSET', 'utf8')
SSH_USER = os.getenv('PYTEST_SSH_USER', None)
SSH_HOST = os.getenv('PYTEST_SSH_HOST', None)
Expand Down