pgcli 4.5.0__tar.gz → 4.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pgcli-4.5.0 → pgcli-4.6.0}/AUTHORS +6 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/PKG-INFO +3 -3
- {pgcli-4.5.0 → pgcli-4.6.0}/changelog.rst +30 -0
- pgcli-4.6.0/pgcli/__init__.py +1 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/main.py +52 -5
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/sqlcompletion.py +25 -2
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/pgcompleter.py +7 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/pgexecute.py +2 -1
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli.egg-info/PKG-INFO +3 -3
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli.egg-info/SOURCES.txt +2 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli.egg-info/requires.txt +1 -1
- pgcli-4.6.0/pgcli.egg-info/scm_file_list.json +122 -0
- pgcli-4.6.0/pgcli.egg-info/scm_version.json +8 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pyproject.toml +2 -5
- pgcli-4.6.0/release.py +228 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_main.py +65 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_pgcompleter.py +16 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_sqlcompletion.py +26 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_ssh_tunnel.py +52 -22
- pgcli-4.5.0/pgcli/__init__.py +0 -1
- pgcli-4.5.0/release.py +0 -132
- {pgcli-4.5.0 → pgcli-4.6.0}/.coveragerc +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.editorconfig +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.git-blame-ignore-revs +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.github/ISSUE_TEMPLATE.md +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.github/workflows/ci.yml +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.github/workflows/codeql.yml +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.github/workflows/codex-review.yml +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.github/workflows/publish.yml +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.gitignore +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/.pre-commit-config.yaml +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/CONTRIBUTING.rst +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/Dockerfile +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/LICENSE.txt +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/MANIFEST.in +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/README.rst +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/RELEASES.md +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/TODO +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/__main__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/auth.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/completion_refresher.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/config.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/explain_output_formatter.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/key_bindings.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/magic.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/__init__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/formatter/__init__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/formatter/sqlformatter.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/parseutils/__init__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/parseutils/ctes.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/parseutils/meta.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/parseutils/tables.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/parseutils/utils.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/pgliterals/__init__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/pgliterals/main.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/pgliterals/pgliterals.json +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/prioritization.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/packages/prompt_utils.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/pgbuffer.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/pgclirc +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/pgstyle.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/pgtoolbar.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli/pyev.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli-completion.bash +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli.egg-info/dependency_links.txt +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli.egg-info/entry_points.txt +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pgcli.egg-info/top_level.txt +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/post-install +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/post-remove +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/pylintrc +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/sanity_checks.txt +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/screenshots/image01.png +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/screenshots/image02.png +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/screenshots/kharkiv-destroyed.jpg +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/screenshots/pgcli.gif +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/setup.cfg +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/conftest.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/__init__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/auto_vertical.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/basic_commands.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/crud_database.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/crud_table.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/db_utils.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/environment.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/expanded.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/fixture_data/help.txt +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/fixture_data/help_commands.txt +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/fixture_data/mock_pg_service.conf +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/fixture_utils.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/iocommands.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/named_queries.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/pgbouncer.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/specials.feature +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/__init__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/auto_vertical.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/basic_commands.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/crud_database.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/crud_table.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/expanded.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/iocommands.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/named_queries.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/pgbouncer.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/specials.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/steps/wrappers.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/features/wrappager.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/formatter/__init__.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/formatter/test_sqlformatter.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/metadata.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/parseutils/test_ctes.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/parseutils/test_function_metadata.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/parseutils/test_parseutils.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/pytest.ini +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_application_name.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_auth.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_completion_refresher.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_config.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_fuzzy_completion.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_init_commands_simple.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_naive_completion.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_pgexecute.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_pgspecial.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_prioritization.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_prompt_utils.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_rowlimit.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_smart_completion_multiple_schemata.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_smart_completion_public_schema_only.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/test_trailing_comments.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tests/utils.py +0 -0
- {pgcli-4.5.0 → pgcli-4.6.0}/tox.ini +0 -0
|
@@ -144,10 +144,16 @@ Contributors:
|
|
|
144
144
|
* Jay Knight (jay-knight)
|
|
145
145
|
* fbdb
|
|
146
146
|
* Charbel Jacquin (charbeljc)
|
|
147
|
+
* Jeronimo Garcia (bechampion)
|
|
147
148
|
* Devadathan M B (devadathanmb)
|
|
148
149
|
* Charalampos Stratakis
|
|
149
150
|
* Laszlo Bimba (bimlas)
|
|
150
151
|
* Anjanna
|
|
152
|
+
* Shayan Golshani (shgol)
|
|
153
|
+
* Tommi Kyntölä (kynde)
|
|
154
|
+
* Diego
|
|
155
|
+
* Chris (ChrisJr404)
|
|
156
|
+
* Pieter Ouwerkerk (pouwerkerk)
|
|
151
157
|
|
|
152
158
|
Creator:
|
|
153
159
|
--------
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgcli
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.6.0
|
|
4
4
|
Summary: CLI for Postgres Database. With auto-completion and syntax highlighting.
|
|
5
5
|
Author-email: Pgcli Core Team <pgcli-dev@googlegroups.com>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
7
|
Project-URL: Homepage, https://pgcli.com
|
|
8
8
|
Classifier: Intended Audience :: Developers
|
|
9
9
|
Classifier: Operating System :: Unix
|
|
@@ -29,7 +29,7 @@ Requires-Dist: Pygments>=2.0
|
|
|
29
29
|
Requires-Dist: prompt_toolkit<4.0.0,>=2.0.6
|
|
30
30
|
Requires-Dist: psycopg>=3.0.14; sys_platform != "win32"
|
|
31
31
|
Requires-Dist: psycopg-binary>=3.0.14; sys_platform == "win32"
|
|
32
|
-
Requires-Dist: sqlparse<0.
|
|
32
|
+
Requires-Dist: sqlparse<0.7,>=0.3.0
|
|
33
33
|
Requires-Dist: configobj>=5.0.6
|
|
34
34
|
Requires-Dist: cli_helpers[styles]>=2.4.0
|
|
35
35
|
Requires-Dist: setproctitle>=1.1.9; sys_platform != "win32" and "CYGWIN" not in sys_platform
|
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
4.6.0 (2026-08-26)
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
Bug fixes:
|
|
5
|
+
----------
|
|
6
|
+
* 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).
|
|
9
|
+
* Suggest columns, not datatypes, after a column literally named ``type`` in a ``SELECT`` list.
|
|
10
|
+
* Allow ``sqlparse`` 0.6.x. sqlparse 0.6.0 fixes several denial-of-service
|
|
11
|
+
issues (CVE-2026-59893, CVE-2026-54284, CVE-2026-71491) and a string-escaping
|
|
12
|
+
bug (CVE-2026-59894); the previous ``<0.6`` cap prevented users from
|
|
13
|
+
installing the fixed release.
|
|
14
|
+
|
|
15
|
+
Features:
|
|
16
|
+
---------
|
|
17
|
+
* Honor the ``PSQL_EDITOR`` environment variable when opening the external
|
|
18
|
+
editor (``\\e``, ``\\ev``, ``\\ef``, ``\\ne``), matching psql's precedence of
|
|
19
|
+
``PSQL_EDITOR``, then ``EDITOR``, then ``VISUAL`` ([issue 1398](https://github.com/dbcli/pgcli/issues/1398)).
|
|
20
|
+
* Add ``\\ne <name>`` to edit a named query in the external editor. Loads the
|
|
21
|
+
named query's SQL into ``$EDITOR``; on save it is written back to the
|
|
22
|
+
``[named queries]`` section, creating it if it does not exist. Complements
|
|
23
|
+
``\\ns`` (save) by making longer queries easier to edit ([issue 1430](https://github.com/dbcli/pgcli/issues/1430)).
|
|
24
|
+
* Enable ``.pgpass`` support for SSH tunnel connections.
|
|
25
|
+
* Preserve original hostname for ``.pgpass`` lookup using PostgreSQL's ``hostaddr`` parameter
|
|
26
|
+
* SSH tunnel endpoint (``127.0.0.1``) is passed via ``hostaddr``, keeping ``host`` for ``.pgpass``
|
|
27
|
+
* Works with both DSN and host/port connection styles
|
|
28
|
+
|
|
1
29
|
4.5.0 (2026-06-02)
|
|
2
30
|
==================
|
|
3
31
|
|
|
@@ -20,6 +48,8 @@ Bug fixes:
|
|
|
20
48
|
* Fix trailing SQL comments preventing query submission and execution.
|
|
21
49
|
* ``SELECT 1; -- note`` now submits correctly in multiline mode
|
|
22
50
|
* ``rstrip(";")`` in ``pgexecute.py`` now handles comments after the semicolon
|
|
51
|
+
* Fix completion crash when tables are created during refresh.
|
|
52
|
+
* Suggest columns after `GROUP BY`, like `ORDER BY` already does.
|
|
23
53
|
|
|
24
54
|
4.4.0 (2025-12-24)
|
|
25
55
|
==================
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.6.0"
|
|
@@ -146,6 +146,17 @@ def notify_callback(notify: Notify):
|
|
|
146
146
|
)
|
|
147
147
|
|
|
148
148
|
|
|
149
|
+
def get_editor():
|
|
150
|
+
"""Pick the external editor for ``\\e``/``\\ev``/``\\ef``/``\\ne``.
|
|
151
|
+
|
|
152
|
+
Mirrors psql, which checks ``PSQL_EDITOR`` first, then ``EDITOR``, then
|
|
153
|
+
``VISUAL``. Returning ``None`` when none are set lets click fall back to
|
|
154
|
+
its platform default, so the behaviour is unchanged for anyone who wasn't
|
|
155
|
+
setting ``PSQL_EDITOR``.
|
|
156
|
+
"""
|
|
157
|
+
return os.environ.get("PSQL_EDITOR") or os.environ.get("EDITOR") or os.environ.get("VISUAL") or None
|
|
158
|
+
|
|
159
|
+
|
|
149
160
|
class PGCli:
|
|
150
161
|
default_prompt = "\\u@\\h:\\d> "
|
|
151
162
|
max_len_prompt = 30
|
|
@@ -319,6 +330,31 @@ class PGCli:
|
|
|
319
330
|
message = f"Named query quiet mode: {status}"
|
|
320
331
|
return [(None, None, None, message)]
|
|
321
332
|
|
|
333
|
+
def edit_named_query(self, pattern, **_):
|
|
334
|
+
r"""Edit (or create) a named query in the external editor (\ne name).
|
|
335
|
+
|
|
336
|
+
Loads the named query's SQL into ``$EDITOR``; on save, persists it back
|
|
337
|
+
to the ``[named queries]`` section. If the name does not exist, the
|
|
338
|
+
editor opens empty and saving creates it.
|
|
339
|
+
"""
|
|
340
|
+
name = pattern.strip()
|
|
341
|
+
if not name:
|
|
342
|
+
return [(None, None, None, "Usage: \\ne <name>")]
|
|
343
|
+
|
|
344
|
+
existing = NamedQueries.instance.get(name)
|
|
345
|
+
sql, message = special.open_external_editor(sql=existing or "", editor=get_editor())
|
|
346
|
+
if message:
|
|
347
|
+
return [(None, None, None, message)]
|
|
348
|
+
|
|
349
|
+
sql = (sql or "").strip()
|
|
350
|
+
if not sql:
|
|
351
|
+
return [(None, None, None, f"{name}: empty query, not saved.")]
|
|
352
|
+
if existing is not None and sql == existing.strip():
|
|
353
|
+
return [(None, None, None, f"{name}: no changes.")]
|
|
354
|
+
|
|
355
|
+
NamedQueries.instance.save(name, sql)
|
|
356
|
+
return [(None, None, None, f"{name}: {'Created' if existing is None else 'Saved'}")]
|
|
357
|
+
|
|
322
358
|
def _is_named_query_execution(self, text):
|
|
323
359
|
"""Check if the command is a named query execution (\n <name>)."""
|
|
324
360
|
text = text.strip()
|
|
@@ -334,6 +370,13 @@ class PGCli:
|
|
|
334
370
|
case_sensitive=True,
|
|
335
371
|
)
|
|
336
372
|
|
|
373
|
+
self.pgspecial.register(
|
|
374
|
+
self.edit_named_query,
|
|
375
|
+
"\\ne",
|
|
376
|
+
"\\ne name",
|
|
377
|
+
"Edit a named query in the external editor.",
|
|
378
|
+
)
|
|
379
|
+
|
|
337
380
|
self.pgspecial.register(
|
|
338
381
|
self.change_db,
|
|
339
382
|
"\\c",
|
|
@@ -652,7 +695,7 @@ class PGCli:
|
|
|
652
695
|
if self.force_passwd_prompt and not passwd:
|
|
653
696
|
passwd = click.prompt("Password for %s" % user, hide_input=True, show_default=False, type=str)
|
|
654
697
|
|
|
655
|
-
key = f"{user}@{host}"
|
|
698
|
+
key = f"{user}@{host}@{port}"
|
|
656
699
|
|
|
657
700
|
if not passwd and auth.keyring:
|
|
658
701
|
passwd = auth.keyring_get_password(key)
|
|
@@ -719,11 +762,15 @@ class PGCli:
|
|
|
719
762
|
self.logger.handlers = logger_handlers
|
|
720
763
|
|
|
721
764
|
atexit.register(self.ssh_tunnel.stop)
|
|
722
|
-
host
|
|
765
|
+
# Preserve original host for .pgpass lookup and SSL certificate verification.
|
|
766
|
+
# Use hostaddr to specify the actual connection endpoint (SSH tunnel).
|
|
767
|
+
hostaddr = "127.0.0.1"
|
|
723
768
|
port = self.ssh_tunnel.local_bind_ports[0]
|
|
724
769
|
|
|
725
770
|
if dsn:
|
|
726
|
-
dsn = make_conninfo(dsn, host=host, port=port)
|
|
771
|
+
dsn = make_conninfo(dsn, host=host, hostaddr=hostaddr, port=port)
|
|
772
|
+
else:
|
|
773
|
+
kwargs["hostaddr"] = hostaddr
|
|
727
774
|
|
|
728
775
|
# Attempt to connect to the database.
|
|
729
776
|
# Note that passwd may be empty on the first attempt. If connection
|
|
@@ -796,7 +843,7 @@ class PGCli:
|
|
|
796
843
|
query = self.pgexecute.view_definition(spec)
|
|
797
844
|
elif editor_command == "\\ef":
|
|
798
845
|
query = self.pgexecute.function_definition(spec)
|
|
799
|
-
sql, message = special.open_external_editor(filename, sql=query)
|
|
846
|
+
sql, message = special.open_external_editor(filename, sql=query, editor=get_editor())
|
|
800
847
|
if message:
|
|
801
848
|
# Something went wrong. Raise an exception and bail.
|
|
802
849
|
raise RuntimeError(message)
|
|
@@ -1099,7 +1146,7 @@ class PGCli:
|
|
|
1099
1146
|
enable_suspend=True,
|
|
1100
1147
|
editing_mode=EditingMode.VI if self.vi_mode else EditingMode.EMACS,
|
|
1101
1148
|
search_ignore_case=True,
|
|
1102
|
-
cursor=ModalCursorShapeConfig(),
|
|
1149
|
+
cursor=ModalCursorShapeConfig() if self.vi_mode else None,
|
|
1103
1150
|
)
|
|
1104
1151
|
|
|
1105
1152
|
return prompt_app
|
|
@@ -278,6 +278,29 @@ def suggest_special(text):
|
|
|
278
278
|
return (Keyword(), Special())
|
|
279
279
|
|
|
280
280
|
|
|
281
|
+
def _is_datatype_keyword(token, stmt):
|
|
282
|
+
"""Decide whether a `type` token is the SQL TYPE keyword or a column name.
|
|
283
|
+
|
|
284
|
+
`type` is a non-reserved word in postgres, so sqlparse tags a column
|
|
285
|
+
literally named "type" as a keyword. The TYPE keyword only introduces a
|
|
286
|
+
datatype in DDL contexts (`ALTER COLUMN bar TYPE`, `SET DATA TYPE`,
|
|
287
|
+
`CREATE TYPE`), never directly inside a DML select/insert/update list.
|
|
288
|
+
"""
|
|
289
|
+
if isinstance(token, str):
|
|
290
|
+
# An explicit internal request for datatype suggestions, e.g. the
|
|
291
|
+
# parenthesized column list in `CREATE TABLE foo (bar <CURSOR>`.
|
|
292
|
+
return True
|
|
293
|
+
|
|
294
|
+
if not token.is_keyword:
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
prev_keyword, _ = find_prev_keyword(stmt.text_before_cursor.rstrip(), n_skip=1)
|
|
298
|
+
if prev_keyword is None:
|
|
299
|
+
return False
|
|
300
|
+
|
|
301
|
+
return prev_keyword.ttype is not sqlparse.tokens.Keyword.DML
|
|
302
|
+
|
|
303
|
+
|
|
281
304
|
def suggest_based_on_last_token(token, stmt):
|
|
282
305
|
if isinstance(token, str):
|
|
283
306
|
token_v = token.lower()
|
|
@@ -383,7 +406,7 @@ def suggest_based_on_last_token(token, stmt):
|
|
|
383
406
|
# E.g. 'UPDATE foo SET'
|
|
384
407
|
return (Column(table_refs=stmt.get_tables(), local_tables=stmt.local_tables),)
|
|
385
408
|
|
|
386
|
-
elif token_v in ("select", "where", "having", "order by", "distinct"):
|
|
409
|
+
elif token_v in ("select", "where", "having", "group by", "order by", "distinct"):
|
|
387
410
|
return _suggest_expression(token_v, stmt)
|
|
388
411
|
elif token_v == "as":
|
|
389
412
|
# Don't suggest anything for aliases
|
|
@@ -492,7 +515,7 @@ def suggest_based_on_last_token(token, stmt):
|
|
|
492
515
|
return suggest_based_on_last_token(prev_keyword, stmt)
|
|
493
516
|
else:
|
|
494
517
|
return ()
|
|
495
|
-
elif token_v
|
|
518
|
+
elif token_v == "::" or (token_v == "type" and _is_datatype_keyword(token, stmt)):
|
|
496
519
|
# ALTER TABLE foo SET DATA TYPE bar
|
|
497
520
|
# SELECT foo::bar
|
|
498
521
|
# Note that tables are a form of composite type in postgresql, so
|
|
@@ -141,6 +141,11 @@ class PGCompleter(Completer):
|
|
|
141
141
|
self.all_completions = set(self.keywords + self.functions)
|
|
142
142
|
|
|
143
143
|
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
|
+
|
|
144
149
|
if name and ((not self.name_pattern.match(name)) or (name.upper() in self.reserved_words) or (name.upper() in self.functions)):
|
|
145
150
|
name = '"%s"' % name
|
|
146
151
|
|
|
@@ -229,6 +234,8 @@ class PGCompleter(Completer):
|
|
|
229
234
|
has_default=has_default,
|
|
230
235
|
default=default,
|
|
231
236
|
)
|
|
237
|
+
metadata.setdefault(schema, {})
|
|
238
|
+
metadata[schema].setdefault(relname, OrderedDict())
|
|
232
239
|
metadata[schema][relname][colname] = column
|
|
233
240
|
self.all_completions.add(colname)
|
|
234
241
|
|
|
@@ -215,7 +215,8 @@ class PGExecute:
|
|
|
215
215
|
new_params.update(kwargs)
|
|
216
216
|
|
|
217
217
|
if new_params["dsn"]:
|
|
218
|
-
|
|
218
|
+
# When using DSN, only keep dsn, password, and hostaddr (for SSH tunnels)
|
|
219
|
+
new_params = {k: v for k, v in new_params.items() if k in ("dsn", "password", "hostaddr")}
|
|
219
220
|
|
|
220
221
|
if new_params["password"]:
|
|
221
222
|
new_params["dsn"] = make_conninfo(new_params["dsn"], password=new_params.pop("password"))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgcli
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.6.0
|
|
4
4
|
Summary: CLI for Postgres Database. With auto-completion and syntax highlighting.
|
|
5
5
|
Author-email: Pgcli Core Team <pgcli-dev@googlegroups.com>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
7
|
Project-URL: Homepage, https://pgcli.com
|
|
8
8
|
Classifier: Intended Audience :: Developers
|
|
9
9
|
Classifier: Operating System :: Unix
|
|
@@ -29,7 +29,7 @@ Requires-Dist: Pygments>=2.0
|
|
|
29
29
|
Requires-Dist: prompt_toolkit<4.0.0,>=2.0.6
|
|
30
30
|
Requires-Dist: psycopg>=3.0.14; sys_platform != "win32"
|
|
31
31
|
Requires-Dist: psycopg-binary>=3.0.14; sys_platform == "win32"
|
|
32
|
-
Requires-Dist: sqlparse<0.
|
|
32
|
+
Requires-Dist: sqlparse<0.7,>=0.3.0
|
|
33
33
|
Requires-Dist: configobj>=5.0.6
|
|
34
34
|
Requires-Dist: cli_helpers[styles]>=2.4.0
|
|
35
35
|
Requires-Dist: setproctitle>=1.1.9; sys_platform != "win32" and "CYGWIN" not in sys_platform
|
|
@@ -47,6 +47,8 @@ pgcli.egg-info/SOURCES.txt
|
|
|
47
47
|
pgcli.egg-info/dependency_links.txt
|
|
48
48
|
pgcli.egg-info/entry_points.txt
|
|
49
49
|
pgcli.egg-info/requires.txt
|
|
50
|
+
pgcli.egg-info/scm_file_list.json
|
|
51
|
+
pgcli.egg-info/scm_version.json
|
|
50
52
|
pgcli.egg-info/top_level.txt
|
|
51
53
|
pgcli/packages/__init__.py
|
|
52
54
|
pgcli/packages/prioritization.py
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
".coveragerc",
|
|
4
|
+
".editorconfig",
|
|
5
|
+
".git-blame-ignore-revs",
|
|
6
|
+
".github/ISSUE_TEMPLATE.md",
|
|
7
|
+
".github/PULL_REQUEST_TEMPLATE.md",
|
|
8
|
+
".github/workflows/ci.yml",
|
|
9
|
+
".github/workflows/codeql.yml",
|
|
10
|
+
".github/workflows/codex-review.yml",
|
|
11
|
+
".github/workflows/publish.yml",
|
|
12
|
+
".gitignore",
|
|
13
|
+
".pre-commit-config.yaml",
|
|
14
|
+
"AUTHORS",
|
|
15
|
+
"CONTRIBUTING.rst",
|
|
16
|
+
"Dockerfile",
|
|
17
|
+
"LICENSE.txt",
|
|
18
|
+
"MANIFEST.in",
|
|
19
|
+
"README.rst",
|
|
20
|
+
"RELEASES.md",
|
|
21
|
+
"TODO",
|
|
22
|
+
"changelog.rst",
|
|
23
|
+
"pgcli-completion.bash",
|
|
24
|
+
"pgcli/__init__.py",
|
|
25
|
+
"pgcli/__main__.py",
|
|
26
|
+
"pgcli/auth.py",
|
|
27
|
+
"pgcli/completion_refresher.py",
|
|
28
|
+
"pgcli/config.py",
|
|
29
|
+
"pgcli/explain_output_formatter.py",
|
|
30
|
+
"pgcli/key_bindings.py",
|
|
31
|
+
"pgcli/magic.py",
|
|
32
|
+
"pgcli/main.py",
|
|
33
|
+
"pgcli/packages/__init__.py",
|
|
34
|
+
"pgcli/packages/formatter/__init__.py",
|
|
35
|
+
"pgcli/packages/formatter/sqlformatter.py",
|
|
36
|
+
"pgcli/packages/parseutils/__init__.py",
|
|
37
|
+
"pgcli/packages/parseutils/ctes.py",
|
|
38
|
+
"pgcli/packages/parseutils/meta.py",
|
|
39
|
+
"pgcli/packages/parseutils/tables.py",
|
|
40
|
+
"pgcli/packages/parseutils/utils.py",
|
|
41
|
+
"pgcli/packages/pgliterals/__init__.py",
|
|
42
|
+
"pgcli/packages/pgliterals/main.py",
|
|
43
|
+
"pgcli/packages/pgliterals/pgliterals.json",
|
|
44
|
+
"pgcli/packages/prioritization.py",
|
|
45
|
+
"pgcli/packages/prompt_utils.py",
|
|
46
|
+
"pgcli/packages/sqlcompletion.py",
|
|
47
|
+
"pgcli/pgbuffer.py",
|
|
48
|
+
"pgcli/pgclirc",
|
|
49
|
+
"pgcli/pgcompleter.py",
|
|
50
|
+
"pgcli/pgexecute.py",
|
|
51
|
+
"pgcli/pgstyle.py",
|
|
52
|
+
"pgcli/pgtoolbar.py",
|
|
53
|
+
"pgcli/pyev.py",
|
|
54
|
+
"post-install",
|
|
55
|
+
"post-remove",
|
|
56
|
+
"pylintrc",
|
|
57
|
+
"pyproject.toml",
|
|
58
|
+
"release.py",
|
|
59
|
+
"sanity_checks.txt",
|
|
60
|
+
"screenshots/image01.png",
|
|
61
|
+
"screenshots/image02.png",
|
|
62
|
+
"screenshots/kharkiv-destroyed.jpg",
|
|
63
|
+
"screenshots/pgcli.gif",
|
|
64
|
+
"tests/conftest.py",
|
|
65
|
+
"tests/features/__init__.py",
|
|
66
|
+
"tests/features/auto_vertical.feature",
|
|
67
|
+
"tests/features/basic_commands.feature",
|
|
68
|
+
"tests/features/crud_database.feature",
|
|
69
|
+
"tests/features/crud_table.feature",
|
|
70
|
+
"tests/features/db_utils.py",
|
|
71
|
+
"tests/features/environment.py",
|
|
72
|
+
"tests/features/expanded.feature",
|
|
73
|
+
"tests/features/fixture_data/help.txt",
|
|
74
|
+
"tests/features/fixture_data/help_commands.txt",
|
|
75
|
+
"tests/features/fixture_data/mock_pg_service.conf",
|
|
76
|
+
"tests/features/fixture_utils.py",
|
|
77
|
+
"tests/features/iocommands.feature",
|
|
78
|
+
"tests/features/named_queries.feature",
|
|
79
|
+
"tests/features/pgbouncer.feature",
|
|
80
|
+
"tests/features/specials.feature",
|
|
81
|
+
"tests/features/steps/__init__.py",
|
|
82
|
+
"tests/features/steps/auto_vertical.py",
|
|
83
|
+
"tests/features/steps/basic_commands.py",
|
|
84
|
+
"tests/features/steps/crud_database.py",
|
|
85
|
+
"tests/features/steps/crud_table.py",
|
|
86
|
+
"tests/features/steps/expanded.py",
|
|
87
|
+
"tests/features/steps/iocommands.py",
|
|
88
|
+
"tests/features/steps/named_queries.py",
|
|
89
|
+
"tests/features/steps/pgbouncer.py",
|
|
90
|
+
"tests/features/steps/specials.py",
|
|
91
|
+
"tests/features/steps/wrappers.py",
|
|
92
|
+
"tests/features/wrappager.py",
|
|
93
|
+
"tests/formatter/__init__.py",
|
|
94
|
+
"tests/formatter/test_sqlformatter.py",
|
|
95
|
+
"tests/metadata.py",
|
|
96
|
+
"tests/parseutils/test_ctes.py",
|
|
97
|
+
"tests/parseutils/test_function_metadata.py",
|
|
98
|
+
"tests/parseutils/test_parseutils.py",
|
|
99
|
+
"tests/pytest.ini",
|
|
100
|
+
"tests/test_application_name.py",
|
|
101
|
+
"tests/test_auth.py",
|
|
102
|
+
"tests/test_completion_refresher.py",
|
|
103
|
+
"tests/test_config.py",
|
|
104
|
+
"tests/test_fuzzy_completion.py",
|
|
105
|
+
"tests/test_init_commands_simple.py",
|
|
106
|
+
"tests/test_main.py",
|
|
107
|
+
"tests/test_naive_completion.py",
|
|
108
|
+
"tests/test_pgcompleter.py",
|
|
109
|
+
"tests/test_pgexecute.py",
|
|
110
|
+
"tests/test_pgspecial.py",
|
|
111
|
+
"tests/test_prioritization.py",
|
|
112
|
+
"tests/test_prompt_utils.py",
|
|
113
|
+
"tests/test_rowlimit.py",
|
|
114
|
+
"tests/test_smart_completion_multiple_schemata.py",
|
|
115
|
+
"tests/test_smart_completion_public_schema_only.py",
|
|
116
|
+
"tests/test_sqlcompletion.py",
|
|
117
|
+
"tests/test_ssh_tunnel.py",
|
|
118
|
+
"tests/test_trailing_comments.py",
|
|
119
|
+
"tests/utils.py",
|
|
120
|
+
"tox.ini"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pgcli"
|
|
3
3
|
authors = [{ name = "Pgcli Core Team", email = "pgcli-dev@googlegroups.com" }]
|
|
4
|
-
license =
|
|
4
|
+
license = "BSD-3-Clause"
|
|
5
5
|
description = "CLI for Postgres Database. With auto-completion and syntax highlighting."
|
|
6
6
|
readme = "README.rst"
|
|
7
7
|
classifiers = [
|
|
@@ -32,7 +32,7 @@ dependencies = [
|
|
|
32
32
|
"prompt_toolkit>=2.0.6,<4.0.0",
|
|
33
33
|
"psycopg >= 3.0.14; sys_platform != 'win32'",
|
|
34
34
|
"psycopg-binary >= 3.0.14; sys_platform == 'win32'",
|
|
35
|
-
"sqlparse >=0.3.0,<0.
|
|
35
|
+
"sqlparse >=0.3.0,<0.7",
|
|
36
36
|
"configobj >= 5.0.6",
|
|
37
37
|
"cli_helpers[styles] >= 2.4.0",
|
|
38
38
|
# setproctitle is used to mask the password when running `ps` in command line.
|
|
@@ -73,9 +73,6 @@ build-backend = "setuptools.build_meta"
|
|
|
73
73
|
[tool.setuptools]
|
|
74
74
|
include-package-data = false
|
|
75
75
|
|
|
76
|
-
[tool.setuptools.dynamic]
|
|
77
|
-
version = { attr = "pgcli.__version__" }
|
|
78
|
-
|
|
79
76
|
[tool.setuptools.packages]
|
|
80
77
|
find = { namespaces = false }
|
|
81
78
|
|