- Convert Python placeholders (``%s``, ``%(name)s``) into Postgres
format (``$1``, ``$2``)
- - return ``query`` (bytes), ``order`` (sequence of names used in the
- query, in the position they appear, in case of named params, else None)
+ - placeholders can be %s or %b (text or binary)
+ - return ``query`` (bytes), ``formats`` (list of formats) ``order``
+ (sequence of names used in the query, in the position they appear, in
+
"""
if not isinstance(query, bytes):
# encoding from str already happened
m = None
for m in _re_placeholder.finditer(query):
pre = query[cur : m.span(0)[0]]
- parts.append([pre, m])
+ parts.append([pre, m, None])
cur = m.span(0)[1]
if m is None:
- parts.append([query, None])
+ parts.append([query, None, None])
else:
- parts.append([query[cur:], None])
+ parts.append([query[cur:], None, None])
# drop the "%%", validate
i = 0
phtype = None
while i < len(parts):
- m = parts[i][1]
+ part = parts[i]
+ m = part[1]
if m is None:
break # last part
ph = m.group(0)
if ph == b"%%":
# unescape '%%' to '%' and merge the parts
- parts[i + 1][0] = parts[i][0] + b"%" + parts[i + 1][0]
+ parts[i + 1][0] = part[0] + b"%" + parts[i + 1][0]
del parts[i]
continue
if ph == b"%(":
"incomplete placeholder: '%'; if you want to use '%' as an"
" operator you can double it up, i.e. use '%%'"
)
- elif ph[-1:] != b"s":
+ elif ph[-1:] not in b"bs":
raise exc.ProgrammingError(
- f"only '%s' and '%(name)s' placeholders allowed, got"
+ f"only '%s' and '%b' placeholders allowed, got"
f" {m.group(0).decode(encoding)}"
)
# Index or name
if m.group(1) is None:
- parts[i][1] = i
+ part[1] = i
else:
- parts[i][1] = m.group(1)
+ part[1] = m.group(1)
+
+ # Binary format
+ part[2] = ph[-1:] == b"b"
if phtype is None:
- phtype = type(parts[i][1])
+ phtype = type(part[1])
else:
- if phtype is not type(parts[i][1]): # noqa
+ if phtype is not type(part[1]): # noqa
raise exc.ProgrammingError(
"positional and named placeholders cannot be mixed"
)
@pytest.mark.parametrize(
"input, want",
[
- (b"", [[b"", None]]),
- (b"foo bar", [[b"foo bar", None]]),
- (b"foo %% bar", [[b"foo % bar", None]]),
- (b"%s", [[b"", 0], [b"", None]]),
- (b"%s foo", [[b"", 0], [b" foo", None]]),
- (b"foo %s", [[b"foo ", 0], [b"", None]]),
- (b"foo %%%s bar", [[b"foo %", 0], [b" bar", None]]),
- (b"foo %(name)s bar", [[b"foo ", b"name"], [b" bar", None]]),
+ (b"", [[b"", None, None]]),
+ (b"foo bar", [[b"foo bar", None, None]]),
+ (b"foo %% bar", [[b"foo % bar", None, None]]),
+ (b"%s", [[b"", 0, False], [b"", None, None]]),
+ (b"%s foo", [[b"", 0, False], [b" foo", None, None]]),
+ (b"%b foo", [[b"", 0, True], [b" foo", None, None]]),
+ (b"foo %s", [[b"foo ", 0, False], [b"", None, None]]),
+ (b"foo %%%s bar", [[b"foo %", 0, False], [b" bar", None, None]]),
(
- b"foo %s%s bar %s baz",
- [[b"foo ", 0], [b"", 1], [b" bar ", 2], [b" baz", None]],
+ b"foo %(name)s bar",
+ [[b"foo ", b"name", False], [b" bar", None, None]],
+ ),
+ (
+ b"foo %(name)s %(name)b bar",
+ [
+ [b"foo ", b"name", False],
+ [b" ", b"name", True],
+ [b" bar", None, None],
+ ],
+ ),
+ (
+ b"foo %s%b bar %s baz",
+ [
+ [b"foo ", 0, False],
+ [b"", 1, True],
+ [b" bar ", 2, False],
+ [b" baz", None, None],
+ ],
),
],
)