pgcli
pgcli copied to clipboard
Connecting via unix socket on a specific DB crashes pgcli
Description
Hello
Installing pgcli 3.5.0 on a Debian server with postgresql 15, when, as unix user postgres, I run a simple pgcli, I get a proper prompt, but as soon as I use pgcli XXX with XXXbeing another database, pgcli crashes while starting because it tries to mix str and bytes.
postgres@pgsql:~$ pgcli
Server: PostgreSQL 15.3 (Debian 15.3-0+deb12u1)
Version: 3.5.0
Home: http://pgcli.com
postgres>
Goodbye!
postgres@pgsql:~$ pgcli icinga2
Server: PostgreSQL 15.3 (Debian 15.3-0+deb12u1)
Version: 3.5.0
Home: http://pgcli.com
Traceback (most recent call last):
File "/usr/bin/pgcli", line 33, in <module>
Exception in thread completion_refresh:
Traceback (most recent call last):
File "/usr/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
sys.exit(load_entry_point('pgcli==3.5.0', 'console_scripts', 'pgcli')())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/click/core.py", line 1130, in __call__
self.run()
File "/usr/lib/python3.11/threading.py", line 975, in run
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3/dist-packages/pgcli/completion_refresher.py", line 68, in _bg_refresh
File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
refresher(completer, executor)
File "/usr/lib/python3/dist-packages/pgcli/completion_refresher.py", line 109, in refresh_schemata
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
completer.set_search_path(executor.search_path())
File "/usr/lib/python3/dist-packages/pgcli/pgcompleter.py", line 306, in set_search_path
return __callback(*args, **kwargs)
self.search_path = self.escaped_names(search_path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/pgcli/main.py", line 1427, in cli
File "/usr/lib/python3/dist-packages/pgcli/pgcompleter.py", line 150, in escaped_names
return [self.escape_name(name) for name in names]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/pgcli/pgcompleter.py", line 150, in <listcomp>
pgcli.run_cli()
File "/usr/lib/python3/dist-packages/pgcli/main.py", line 788, in run_cli
return [self.escape_name(name) for name in names]
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/pgcli/pgcompleter.py", line 131, in escape_name
text = self.prompt_app.prompt()
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/shortcuts/prompt.py", line 1034, in prompt
(not self.name_pattern.match(name))
TypeError: cannot use a string pattern on a bytes-like object
return self.app.run(
^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/application/application.py", line 978, in run
return loop.run_until_complete(
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/application/application.py", line 885, in run_async
return await _run_async(f)
^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/application/application.py", line 743, in _run_async
self._redraw()
File "/usr/lib/python3/dist-packages/prompt_toolkit/application/application.py", line 557, in _redraw
self.context.copy().run(run_in_context)
File "/usr/lib/python3/dist-packages/prompt_toolkit/application/application.py", line 540, in run_in_context
self.renderer.render(self, self.layout)
File "/usr/lib/python3/dist-packages/prompt_toolkit/renderer.py", line 640, in render
layout.container.preferred_height(size.columns, size.rows).preferred,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/layout/containers.py", line 326, in preferred_height
dimensions = [
^
File "/usr/lib/python3/dist-packages/prompt_toolkit/layout/containers.py", line 327, in <listcomp>
c.preferred_height(width, max_available_height) for c in self._all_children
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/layout/containers.py", line 796, in preferred_height
return self.content.preferred_height(width, max_available_height)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/layout/containers.py", line 326, in preferred_height
dimensions = [
^
File "/usr/lib/python3/dist-packages/prompt_toolkit/layout/containers.py", line 327, in <listcomp>
c.preferred_height(width, max_available_height) for c in self._all_children
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/layout/containers.py", line 2642, in preferred_height
if self.filter():
^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/filters/base.py", line 210, in __call__
return self.func()
^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/shortcuts/prompt.py", line 168, in has_before_fragments
for fragment, char, *_ in get_prompt_text():
^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/shortcuts/prompt.py", line 1279, in _get_prompt
return to_formatted_text(self.message, style="class:prompt")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/prompt_toolkit/formatted_text/base.py", line 81, in to_formatted_text
return to_formatted_text(value(), style=style)
^^^^^^^
File "/usr/lib/python3/dist-packages/pgcli/main.py", line 851, in get_message
prompt = self.get_prompt(prompt_format)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/pgcli/main.py", line 1115, in get_prompt
string = string.replace("\\H", self.pgexecute.host or "(none)")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: replace() argument 2 must be str, not bytes
postgres@pgsql:~$ ;1R
As you see, there are two type errors, and both relate to mixing str and bytes.
After poking a bit, it seems that when I give another database as an argument, pgcli connects to it and then makes queries to the postgres database to get the unix socket configuration and other stufs from the pg_settings database. In the case of a connection to the postgres database, the cursor fetches strings (eg in pgexecute.get_socket_directory function), while when connected to another database, the cursor fetches bytes.
I think some sanitizing is needed.
Your environment
- [x] Please provide your OS and version information. Debian 12
- [x] Please provide your CLI version. 3.5.0
- [x] What is the output of
pip freezecommand: nothing, I don't use pip. psycopg version is 3.1.7
@P-EB Thank you for reporting. What's the python version, and how did you install pgcli (you said it was not pip)?
Via apt, as I'm using Debian.
Python3 version is 3.11.5
@j-bennet in case you did not see it I replied to your question.