|
1 | 1 | # MySQL Connector/Python - MySQL driver written in Python.
|
2 |
| -# Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. |
| 2 | +# Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. |
3 | 3 |
|
4 | 4 | # MySQL Connector/Python is licensed under the terms of the GPLv2
|
5 | 5 | # <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
|
|
44 | 44 | re.I | re.M | re.S)
|
45 | 45 | RE_SQL_INSERT_VALUES = re.compile(r'.*VALUES\s*(\(.*\)).*', re.I | re.M | re.S)
|
46 | 46 | RE_PY_PARAM = re.compile(b'(%s)')
|
| 47 | +RE_PY_MAPPING_PARAM = re.compile( |
| 48 | + br''' |
| 49 | + % |
| 50 | + \((?P<mapping_key>[^)]+)\) |
| 51 | + (?P<conversion_type>[diouxXeEfFgGcrs%]) |
| 52 | + ''', |
| 53 | + re.X |
| 54 | +) |
47 | 55 | RE_SQL_SPLIT_STMTS = re.compile(
|
48 | 56 | b''';(?=(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$)''')
|
49 | 57 | RE_SQL_FIND_PARAM = re.compile(
|
@@ -75,6 +83,29 @@ def remaining(self):
|
75 | 83 | return len(self.params) - self.index
|
76 | 84 |
|
77 | 85 |
|
| 86 | +def _bytestr_format_dict(bytestr, value_dict): |
| 87 | + """ |
| 88 | + >>> _bytestr_format_dict(b'%(a)s', {b'a': b'foobar'}) |
| 89 | + b'foobar |
| 90 | + >>> _bytestr_format_dict(b'%%(a)s', {b'a': b'foobar'}) |
| 91 | + b'%%(a)s' |
| 92 | + >>> _bytestr_format_dict(b'%%%(a)s', {b'a': b'foobar'}) |
| 93 | + b'%%foobar' |
| 94 | + >>> _bytestr_format_dict(b'%(x)s %(y)s', |
| 95 | + ... {b'x': b'x=%(y)s', b'y': b'y=%(x)s'}) |
| 96 | + b'x=%(y)s y=%(x)s' |
| 97 | + """ |
| 98 | + def replace(matchobj): |
| 99 | + groups = matchobj.groupdict() |
| 100 | + if groups["conversion_type"] == b"%": |
| 101 | + return b"%" |
| 102 | + if groups["conversion_type"] == b"s": |
| 103 | + return value_dict[groups["mapping_key"]] |
| 104 | + raise ValueError("Unsupported conversion_type: {0}" |
| 105 | + "".format(groups["conversion_type"])) |
| 106 | + return RE_PY_MAPPING_PARAM.sub(replace, bytestr) |
| 107 | + |
| 108 | + |
78 | 109 | class CursorBase(MySQLCursorAbstract):
|
79 | 110 | """
|
80 | 111 | Base for defining MySQLCursor. This class is a skeleton and defines
|
@@ -360,9 +391,9 @@ def _process_params_dict(self, params):
|
360 | 391 | conv = escape(conv)
|
361 | 392 | conv = quote(conv)
|
362 | 393 | if PY2:
|
363 |
| - res["%({0})s".format(key)] = conv |
| 394 | + res[key] = conv |
364 | 395 | else:
|
365 |
| - res["%({0})s".format(key).encode()] = conv |
| 396 | + res[key.encode()] = conv |
366 | 397 | except Exception as err:
|
367 | 398 | raise errors.ProgrammingError(
|
368 | 399 | "Failed processing pyformat-parameters; %s" % err)
|
@@ -497,8 +528,8 @@ def execute(self, operation, params=None, multi=False):
|
497 | 528 |
|
498 | 529 | if params is not None:
|
499 | 530 | if isinstance(params, dict):
|
500 |
| - for key, value in self._process_params_dict(params).items(): |
501 |
| - stmt = stmt.replace(key, value) |
| 531 | + stmt = _bytestr_format_dict( |
| 532 | + stmt, self._process_params_dict(params)) |
502 | 533 | elif isinstance(params, (list, tuple)):
|
503 | 534 | psub = _ParamSubstitutor(self._process_params(params))
|
504 | 535 | stmt = RE_PY_PARAM.sub(psub, stmt)
|
@@ -551,8 +582,8 @@ def remove_comments(match):
|
551 | 582 | for params in seq_params:
|
552 | 583 | tmp = fmt
|
553 | 584 | if isinstance(params, dict):
|
554 |
| - for key, value in self._process_params_dict(params).items(): |
555 |
| - tmp = tmp.replace(key, value) |
| 585 | + tmp = _bytestr_format_dict( |
| 586 | + tmp, self._process_params_dict(params)) |
556 | 587 | else:
|
557 | 588 | psub = _ParamSubstitutor(self._process_params(params))
|
558 | 589 | tmp = RE_PY_PARAM.sub(psub, tmp)
|
|
0 commit comments