Skip to content

Commit 59e1cfc

Browse files
committed
fix: decode bytes identifiers in completion metadata
When the client encoding is one psycopg cannot decode (e.g. SQL_ASCII), catalog identifiers such as schema names come back as bytes. escape_name then matched a str regex against them and raised "TypeError: cannot use a string pattern on a bytes-like object", killing the completion_refresher thread so completions never loaded. escape_name now decodes bytes with the replacement error handler before matching, so completion degrades gracefully instead of crashing. Closes #1405
1 parent fab13f0 commit 59e1cfc

3 files changed

Lines changed: 23 additions & 0 deletions

File tree

changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Upcoming (TBD)
44
Bug fixes:
55
----------
66
* Restore cursor shape behaviour for Emacs mode
7+
* Fix ``TypeError: cannot use a string pattern on a bytes-like object`` when
8+
completion metadata comes back as bytes (e.g. ``SQL_ASCII`` client encoding).
79

810
Features:
911
---------

pgcli/pgcompleter.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ def __init__(self, smart_completion=True, pgspecial=None, settings=None):
141141
self.all_completions = set(self.keywords + self.functions)
142142

143143
def escape_name(self, name):
144+
if isinstance(name, bytes):
145+
# Identifiers come back as bytes when the client encoding is one
146+
# psycopg cannot decode (e.g. SQL_ASCII), see issue #1405.
147+
name = name.decode("utf-8", "replace")
148+
144149
if name and ((not self.name_pattern.match(name)) or (name.upper() in self.reserved_words) or (name.upper() in self.functions)):
145150
name = '"%s"' % name
146151

tests/test_pgcompleter.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,19 @@ def test_generate_alias_prefers_alias_over_upper_case_name(table_name, alias_map
9292
)
9393
def test_generate_alias_prefers_upper_case_name_over_underscore_name(table_name, alias):
9494
assert pgcompleter.generate_alias(table_name) == alias
95+
96+
97+
@pytest.mark.parametrize(
98+
"name, expected",
99+
[
100+
(b"pg_catalog", "pg_catalog"),
101+
(b"public", "public"),
102+
(b"Mixed Case", '"Mixed Case"'),
103+
(b"select", '"select"'),
104+
],
105+
)
106+
def test_escape_name_accepts_bytes(name, expected):
107+
"""Identifiers arrive as bytes under encodings psycopg cannot decode."""
108+
completer = pgcompleter.PGCompleter()
109+
assert completer.escape_name(name) == expected
110+
assert completer.escaped_names([name]) == [expected]

0 commit comments

Comments
 (0)