dbcli / pgcli

Postgres CLI with autocompletion and syntax highlighting
http://pgcli.com
BSD 3-Clause "New" or "Revised" License
12.16k stars 556 forks source link

Connecting via unix socket on a specific DB crashes pgcli #1422

Open P-EB opened 1 year ago

P-EB commented 1 year ago

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

j-bennet commented 1 year ago

@P-EB Thank you for reporting. What's the python version, and how did you install pgcli (you said it was not pip)?

P-EB commented 1 year ago

Via apt, as I'm using Debian.

Python3 version is 3.11.5

P-EB commented 1 year ago

@j-bennet in case you did not see it I replied to your question.