pgcli 4.4.0__tar.gz → 4.5.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.4.0 → pgcli-4.5.0}/.github/PULL_REQUEST_TEMPLATE.md +1 -2
- {pgcli-4.4.0 → pgcli-4.5.0}/.github/workflows/ci.yml +1 -1
- pgcli-4.5.0/.github/workflows/codex-review.yml +91 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/.github/workflows/publish.yml +3 -3
- {pgcli-4.4.0 → pgcli-4.5.0}/.pre-commit-config.yaml +1 -1
- {pgcli-4.4.0 → pgcli-4.5.0}/AUTHORS +4 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/CONTRIBUTING.rst +0 -37
- {pgcli-4.4.0 → pgcli-4.5.0}/Dockerfile +1 -1
- {pgcli-4.4.0 → pgcli-4.5.0}/PKG-INFO +11 -11
- {pgcli-4.4.0 → pgcli-4.5.0}/README.rst +7 -6
- {pgcli-4.4.0 → pgcli-4.5.0}/changelog.rst +24 -1
- pgcli-4.5.0/pgcli/__init__.py +1 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/magic.py +1 -1
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/main.py +73 -11
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/__init__.py +2 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/ctes.py +3 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/tables.py +3 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/utils.py +3 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/pgliterals/pgliterals.json +1 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/prioritization.py +2 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/sqlcompletion.py +3 -1
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgbuffer.py +6 -2
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgclirc +9 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgexecute.py +23 -3
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/PKG-INFO +11 -11
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/SOURCES.txt +2 -1
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/requires.txt +1 -1
- {pgcli-4.4.0 → pgcli-4.5.0}/pyproject.toml +5 -4
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/conftest.py +8 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_main.py +41 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_naive_completion.py +7 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_pgexecute.py +9 -9
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_rowlimit.py +26 -2
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_smart_completion_public_schema_only.py +6 -0
- pgcli-4.5.0/tests/test_trailing_comments.py +47 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tox.ini +11 -0
- pgcli-4.4.0/Vagrantfile +0 -138
- pgcli-4.4.0/pgcli/__init__.py +0 -1
- {pgcli-4.4.0 → pgcli-4.5.0}/.coveragerc +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/.editorconfig +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/.git-blame-ignore-revs +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/.github/ISSUE_TEMPLATE.md +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/.github/workflows/codeql.yml +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/.gitignore +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/LICENSE.txt +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/MANIFEST.in +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/RELEASES.md +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/TODO +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/__main__.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/auth.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/completion_refresher.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/config.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/explain_output_formatter.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/key_bindings.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/__init__.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/formatter/__init__.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/formatter/sqlformatter.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/meta.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/pgliterals/__init__.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/pgliterals/main.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/prompt_utils.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgcompleter.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgstyle.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgtoolbar.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pyev.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli-completion.bash +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/dependency_links.txt +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/entry_points.txt +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/top_level.txt +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/post-install +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/post-remove +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/pylintrc +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/release.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/sanity_checks.txt +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/image01.png +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/image02.png +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/kharkiv-destroyed.jpg +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/pgcli.gif +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/setup.cfg +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/__init__.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/auto_vertical.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/basic_commands.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/crud_database.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/crud_table.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/db_utils.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/environment.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/expanded.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_data/help.txt +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_data/help_commands.txt +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_data/mock_pg_service.conf +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_utils.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/iocommands.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/named_queries.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/pgbouncer.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/specials.feature +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/__init__.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/auto_vertical.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/basic_commands.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/crud_database.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/crud_table.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/expanded.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/iocommands.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/named_queries.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/pgbouncer.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/specials.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/wrappers.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/wrappager.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/formatter/__init__.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/formatter/test_sqlformatter.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/metadata.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/parseutils/test_ctes.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/parseutils/test_function_metadata.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/parseutils/test_parseutils.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/pytest.ini +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_application_name.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_auth.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_completion_refresher.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_config.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_fuzzy_completion.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_init_commands_simple.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_pgcompleter.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_pgspecial.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_prioritization.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_prompt_utils.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_smart_completion_multiple_schemata.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_sqlcompletion.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_ssh_tunnel.py +0 -0
- {pgcli-4.4.0 → pgcli-4.5.0}/tests/utils.py +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
## Description
|
|
2
2
|
<!--- Describe your changes in detail. -->
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
4
|
## Checklist
|
|
7
5
|
<!--- We appreciate your help and want to give you credit. Please take a moment to put an `x` in the boxes below as you complete them. -->
|
|
8
6
|
- [ ] I've added this contribution to the `changelog.rst`.
|
|
9
7
|
- [ ] I've added my name to the `AUTHORS` file (or it's already there).
|
|
10
8
|
<!-- We would appreciate if you comply with our code style guidelines. -->
|
|
11
9
|
- [ ] I installed pre-commit hooks (`pip install pre-commit && pre-commit install`).
|
|
10
|
+
- [ ] I verified that my changes work as expected (this may include manually testing them in your local environment, or in other available environments). Cross this out if not relevant (for example, if you're making a documentation change).
|
|
12
11
|
- [x] Please squash merge this pull request (uncheck if you'd like us to merge as multiple commits)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
name: Codex Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types: [opened, labeled, reopened, ready_for_review]
|
|
6
|
+
paths-ignore:
|
|
7
|
+
- '**.md'
|
|
8
|
+
- '**.rst'
|
|
9
|
+
- 'LICENSE.txt'
|
|
10
|
+
- 'doc/**/*.txt'
|
|
11
|
+
- '**/AUTHORS'
|
|
12
|
+
- '**/SPONSORS'
|
|
13
|
+
- '**/TIPS'
|
|
14
|
+
- 'pgcli-completion.bash'
|
|
15
|
+
- 'sanity_checks.txt'
|
|
16
|
+
- 'post-install'
|
|
17
|
+
- 'post-remove'
|
|
18
|
+
- 'screenshots/**'
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
codex-review:
|
|
22
|
+
if: github.event.pull_request.draft == false || (github.event.action == 'labeled' && contains(github.event.pull_request.labels.*.name, 'codex'))
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
outputs:
|
|
27
|
+
final_message: ${{ steps.run_codex.outputs.final-message }}
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- name: Check out PR merge commit
|
|
31
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
32
|
+
with:
|
|
33
|
+
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
|
34
|
+
|
|
35
|
+
- name: Fetch base and head refs
|
|
36
|
+
run: |
|
|
37
|
+
git fetch --no-tags origin \
|
|
38
|
+
${{ github.event.pull_request.base.ref }} \
|
|
39
|
+
+refs/pull/${{ github.event.pull_request.number }}/head
|
|
40
|
+
|
|
41
|
+
- name: Run Codex review
|
|
42
|
+
id: run_codex
|
|
43
|
+
uses: openai/codex-action@v1
|
|
44
|
+
env:
|
|
45
|
+
# Use env variables to handle untrusted metadata safely
|
|
46
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
47
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
48
|
+
with:
|
|
49
|
+
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
|
|
50
|
+
prompt: |
|
|
51
|
+
You are reviewing PR #${{ github.event.pull_request.number }} for ${{ github.repository }}.
|
|
52
|
+
|
|
53
|
+
Only review changes introduced by this PR:
|
|
54
|
+
git log --oneline ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}
|
|
55
|
+
|
|
56
|
+
Focus on:
|
|
57
|
+
- correctness bugs and regressions
|
|
58
|
+
- security concerns
|
|
59
|
+
- missing tests or edge cases
|
|
60
|
+
|
|
61
|
+
Keep feedback concise and actionable.
|
|
62
|
+
|
|
63
|
+
Pull request title and body:
|
|
64
|
+
----
|
|
65
|
+
$PR_TITLE
|
|
66
|
+
$PR_BODY
|
|
67
|
+
|
|
68
|
+
post-feedback:
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
needs: codex-review
|
|
71
|
+
if: needs.codex-review.outputs.final_message != ''
|
|
72
|
+
permissions:
|
|
73
|
+
issues: write
|
|
74
|
+
pull-requests: write
|
|
75
|
+
|
|
76
|
+
steps:
|
|
77
|
+
- name: Post Codex review as PR comment
|
|
78
|
+
uses: actions/github-script@v8
|
|
79
|
+
env:
|
|
80
|
+
CODEX_FINAL_MESSAGE: |
|
|
81
|
+
${{ format('## Codex Review
|
|
82
|
+
{0}', needs.codex-review.outputs.final_message) }}
|
|
83
|
+
with:
|
|
84
|
+
github-token: ${{ github.token }}
|
|
85
|
+
script: |
|
|
86
|
+
await github.rest.issues.createComment({
|
|
87
|
+
owner: context.repo.owner,
|
|
88
|
+
repo: context.repo.repo,
|
|
89
|
+
issue_number: context.payload.pull_request.number,
|
|
90
|
+
body: process.env.CODEX_FINAL_MESSAGE,
|
|
91
|
+
});
|
|
@@ -13,7 +13,7 @@ jobs:
|
|
|
13
13
|
|
|
14
14
|
strategy:
|
|
15
15
|
matrix:
|
|
16
|
-
python-version: ["3.
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
17
17
|
|
|
18
18
|
services:
|
|
19
19
|
postgres:
|
|
@@ -68,7 +68,7 @@ jobs:
|
|
|
68
68
|
python-version: '3.13'
|
|
69
69
|
|
|
70
70
|
- name: Install dependencies
|
|
71
|
-
run: uv sync --all-extras -p 3.
|
|
71
|
+
run: uv sync --all-extras -p 3.14
|
|
72
72
|
|
|
73
73
|
- name: Build
|
|
74
74
|
run: uv build
|
|
@@ -94,4 +94,4 @@ jobs:
|
|
|
94
94
|
name: python-packages
|
|
95
95
|
path: dist/
|
|
96
96
|
- name: Publish to PyPI
|
|
97
|
-
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
|
97
|
+
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
|
@@ -101,43 +101,6 @@ Within the project, create a file: .vscode\\launch.json like below.
|
|
|
101
101
|
]
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
Building RPM and DEB packages
|
|
105
|
-
-----------------------------
|
|
106
|
-
|
|
107
|
-
You will need Vagrant 1.7.2 or higher. In the project root there is a
|
|
108
|
-
Vagrantfile that is setup to do multi-vm provisioning. If you're setting things
|
|
109
|
-
up for the first time, then do:
|
|
110
|
-
|
|
111
|
-
::
|
|
112
|
-
|
|
113
|
-
$ version=x.y.z vagrant up debian
|
|
114
|
-
$ version=x.y.z vagrant up centos
|
|
115
|
-
|
|
116
|
-
If you already have those VMs setup and you're merely creating a new version of
|
|
117
|
-
DEB or RPM package, then you can do:
|
|
118
|
-
|
|
119
|
-
::
|
|
120
|
-
|
|
121
|
-
$ version=x.y.z vagrant provision
|
|
122
|
-
|
|
123
|
-
That will create a .deb file and a .rpm file.
|
|
124
|
-
|
|
125
|
-
The deb package can be installed as follows:
|
|
126
|
-
|
|
127
|
-
::
|
|
128
|
-
|
|
129
|
-
$ sudo dpkg -i pgcli*.deb # if dependencies are available.
|
|
130
|
-
|
|
131
|
-
or
|
|
132
|
-
|
|
133
|
-
$ sudo apt-get install -f pgcli*.deb # if dependencies are not available.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
The rpm package can be installed as follows:
|
|
137
|
-
|
|
138
|
-
::
|
|
139
|
-
|
|
140
|
-
$ sudo yum install pgcli*.rpm
|
|
141
104
|
|
|
142
105
|
Running the integration tests
|
|
143
106
|
-----------------------------
|
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgcli
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.5.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
6
|
License: BSD
|
|
7
7
|
Project-URL: Homepage, https://pgcli.com
|
|
8
8
|
Classifier: Intended Audience :: Developers
|
|
9
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
10
9
|
Classifier: Operating System :: Unix
|
|
11
10
|
Classifier: Programming Language :: Python
|
|
12
11
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
17
|
Classifier: Programming Language :: SQL
|
|
19
18
|
Classifier: Topic :: Database
|
|
20
19
|
Classifier: Topic :: Database :: Front-Ends
|
|
21
20
|
Classifier: Topic :: Software Development
|
|
22
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.10
|
|
24
23
|
Description-Content-Type: text/x-rst
|
|
25
24
|
License-File: LICENSE.txt
|
|
26
25
|
License-File: AUTHORS
|
|
27
26
|
Requires-Dist: pgspecial>=2.0.0
|
|
28
|
-
Requires-Dist: click
|
|
27
|
+
Requires-Dist: click!=8.1.8,!=8.2.*,!=8.3.0,<9,>=4.1
|
|
29
28
|
Requires-Dist: Pygments>=2.0
|
|
30
29
|
Requires-Dist: prompt_toolkit<4.0.0,>=2.0.6
|
|
31
30
|
Requires-Dist: psycopg>=3.0.14; sys_platform != "win32"
|
|
@@ -82,9 +81,9 @@ A REPL for Postgres
|
|
|
82
81
|
|
|
83
82
|
This is a postgres client that does auto-completion and syntax highlighting.
|
|
84
83
|
|
|
85
|
-
Home Page:
|
|
84
|
+
Home Page: https://pgcli.com
|
|
86
85
|
|
|
87
|
-
MySQL Equivalent:
|
|
86
|
+
MySQL Equivalent: https://mycli.net
|
|
88
87
|
|
|
89
88
|
.. image:: screenshots/pgcli.gif
|
|
90
89
|
.. image:: screenshots/image01.png
|
|
@@ -213,8 +212,8 @@ https://github.com/dbcli/pgcli/blob/main/CONTRIBUTING.rst
|
|
|
213
212
|
|
|
214
213
|
Please feel free to reach out to us if you need help.
|
|
215
214
|
|
|
216
|
-
* Amjith, pgcli author: amjith.r@gmail.com, Twitter: `@amjithr <
|
|
217
|
-
* Irina, pgcli maintainer: i.chernyavska@gmail.com, Twitter: `@irinatruong <
|
|
215
|
+
* Amjith, pgcli author: amjith.r@gmail.com, Twitter: `@amjithr <https://twitter.com/amjithr>`_
|
|
216
|
+
* Irina, pgcli maintainer: i.chernyavska@gmail.com, Twitter: `@irinatruong <https://twitter.com/irinatruong>`_
|
|
218
217
|
|
|
219
218
|
Detailed Installation Instructions:
|
|
220
219
|
-----------------------------------
|
|
@@ -395,17 +394,18 @@ Pgcli dropped support for:
|
|
|
395
394
|
|
|
396
395
|
* Python<3.8 as of 4.0.0.
|
|
397
396
|
* Python<3.9 as of 4.2.0.
|
|
397
|
+
* Python<10 as of 4.5.0.
|
|
398
398
|
|
|
399
399
|
Thanks:
|
|
400
400
|
-------
|
|
401
401
|
|
|
402
402
|
A special thanks to `Jonathan Slenders <https://twitter.com/jonathan_s>`_ for
|
|
403
|
-
creating `Python Prompt Toolkit <
|
|
403
|
+
creating `Python Prompt Toolkit <https://github.com/jonathanslenders/python-prompt-toolkit>`_,
|
|
404
404
|
which is quite literally the backbone library, that made this app possible.
|
|
405
405
|
Jonathan has also provided valuable feedback and support during the development
|
|
406
406
|
of this app.
|
|
407
407
|
|
|
408
|
-
`Click <
|
|
408
|
+
`Click <https://click.pocoo.org/>`_ is used for command line option parsing
|
|
409
409
|
and printing error messages.
|
|
410
410
|
|
|
411
411
|
Thanks to `psycopg <https://www.psycopg.org/>`_ for providing a rock solid
|
|
@@ -28,9 +28,9 @@ A REPL for Postgres
|
|
|
28
28
|
|
|
29
29
|
This is a postgres client that does auto-completion and syntax highlighting.
|
|
30
30
|
|
|
31
|
-
Home Page:
|
|
31
|
+
Home Page: https://pgcli.com
|
|
32
32
|
|
|
33
|
-
MySQL Equivalent:
|
|
33
|
+
MySQL Equivalent: https://mycli.net
|
|
34
34
|
|
|
35
35
|
.. image:: screenshots/pgcli.gif
|
|
36
36
|
.. image:: screenshots/image01.png
|
|
@@ -159,8 +159,8 @@ https://github.com/dbcli/pgcli/blob/main/CONTRIBUTING.rst
|
|
|
159
159
|
|
|
160
160
|
Please feel free to reach out to us if you need help.
|
|
161
161
|
|
|
162
|
-
* Amjith, pgcli author: amjith.r@gmail.com, Twitter: `@amjithr <
|
|
163
|
-
* Irina, pgcli maintainer: i.chernyavska@gmail.com, Twitter: `@irinatruong <
|
|
162
|
+
* Amjith, pgcli author: amjith.r@gmail.com, Twitter: `@amjithr <https://twitter.com/amjithr>`_
|
|
163
|
+
* Irina, pgcli maintainer: i.chernyavska@gmail.com, Twitter: `@irinatruong <https://twitter.com/irinatruong>`_
|
|
164
164
|
|
|
165
165
|
Detailed Installation Instructions:
|
|
166
166
|
-----------------------------------
|
|
@@ -341,17 +341,18 @@ Pgcli dropped support for:
|
|
|
341
341
|
|
|
342
342
|
* Python<3.8 as of 4.0.0.
|
|
343
343
|
* Python<3.9 as of 4.2.0.
|
|
344
|
+
* Python<10 as of 4.5.0.
|
|
344
345
|
|
|
345
346
|
Thanks:
|
|
346
347
|
-------
|
|
347
348
|
|
|
348
349
|
A special thanks to `Jonathan Slenders <https://twitter.com/jonathan_s>`_ for
|
|
349
|
-
creating `Python Prompt Toolkit <
|
|
350
|
+
creating `Python Prompt Toolkit <https://github.com/jonathanslenders/python-prompt-toolkit>`_,
|
|
350
351
|
which is quite literally the backbone library, that made this app possible.
|
|
351
352
|
Jonathan has also provided valuable feedback and support during the development
|
|
352
353
|
of this app.
|
|
353
354
|
|
|
354
|
-
`Click <
|
|
355
|
+
`Click <https://click.pocoo.org/>`_ is used for command line option parsing
|
|
355
356
|
and printing error messages.
|
|
356
357
|
|
|
357
358
|
Thanks to `psycopg <https://www.psycopg.org/>`_ for providing a rock solid
|
|
@@ -1,5 +1,28 @@
|
|
|
1
|
+
4.5.0 (2026-06-02)
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
Features:
|
|
5
|
+
---------
|
|
6
|
+
* Add support for `\\T` prompt escape sequence to display transaction status (similar to psql's `%x`).
|
|
7
|
+
* Add cursor shape support for vi mode. When ``vi = True``, the terminal cursor now
|
|
8
|
+
reflects the current editing mode: beam in INSERT, block in NORMAL, underline in REPLACE.
|
|
9
|
+
Uses prompt_toolkit's ``ModalCursorShapeConfig``.
|
|
10
|
+
* Add the option to force-quit pgcli when a transaction is in progress.
|
|
11
|
+
* Add support of Python 3.14.
|
|
12
|
+
* Drop support of Python 3.9.
|
|
13
|
+
|
|
14
|
+
Bug fixes:
|
|
15
|
+
----------
|
|
16
|
+
* Add `VERSION` to built-in function completion so `SELECT VERSION();` is suggested.
|
|
17
|
+
* Hide timezone notice at startup when local and server timezones are the same.
|
|
18
|
+
* Let `sqlparse` accept arbitrarily-large queries.
|
|
19
|
+
* Respect user-specified `LIMIT` clauses when the limit value starts on a new line.
|
|
20
|
+
* Fix trailing SQL comments preventing query submission and execution.
|
|
21
|
+
* ``SELECT 1; -- note`` now submits correctly in multiline mode
|
|
22
|
+
* ``rstrip(";")`` in ``pgexecute.py`` now handles comments after the semicolon
|
|
23
|
+
|
|
1
24
|
4.4.0 (2025-12-24)
|
|
2
|
-
|
|
25
|
+
==================
|
|
3
26
|
|
|
4
27
|
Features:
|
|
5
28
|
---------
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.5.0"
|
|
@@ -40,7 +40,7 @@ def pgcli_line_magic(line):
|
|
|
40
40
|
# from the sqlalchemy connection, so just grab the url and make a
|
|
41
41
|
# new connection
|
|
42
42
|
pgcli = PGCli()
|
|
43
|
-
u = conn.
|
|
43
|
+
u = conn.url
|
|
44
44
|
_logger.debug("New pgcli: %r", str(u))
|
|
45
45
|
|
|
46
46
|
pgcli.connect_uri(str(u._replace(drivername="postgres")))
|
|
@@ -17,7 +17,6 @@ import itertools
|
|
|
17
17
|
import pathlib
|
|
18
18
|
import platform
|
|
19
19
|
from time import time, sleep
|
|
20
|
-
from typing import Optional
|
|
21
20
|
|
|
22
21
|
from cli_helpers.tabular_output import TabularOutputFormatter
|
|
23
22
|
from cli_helpers.tabular_output.preprocessors import (
|
|
@@ -28,6 +27,8 @@ from cli_helpers.tabular_output.preprocessors import (
|
|
|
28
27
|
from cli_helpers.utils import strip_ansi
|
|
29
28
|
from .explain_output_formatter import ExplainOutputFormatter
|
|
30
29
|
import click
|
|
30
|
+
import sqlparse
|
|
31
|
+
from sqlparse import tokens as sqlparse_tokens
|
|
31
32
|
import tzlocal
|
|
32
33
|
|
|
33
34
|
try:
|
|
@@ -48,6 +49,7 @@ from prompt_toolkit.layout.processors import (
|
|
|
48
49
|
)
|
|
49
50
|
from prompt_toolkit.history import FileHistory
|
|
50
51
|
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
|
52
|
+
from prompt_toolkit.cursor_shapes import ModalCursorShapeConfig
|
|
51
53
|
from pygments.lexers.sql import PostgresLexer
|
|
52
54
|
|
|
53
55
|
from pgspecial.main import PGSpecial, NO_QUERY, PAGER_OFF, PAGER_LONG_OUTPUT
|
|
@@ -183,8 +185,8 @@ class PGCli:
|
|
|
183
185
|
prompt_dsn=None,
|
|
184
186
|
auto_vertical_output=False,
|
|
185
187
|
warn=None,
|
|
186
|
-
ssh_tunnel_url:
|
|
187
|
-
log_file:
|
|
188
|
+
ssh_tunnel_url: str | None = None,
|
|
189
|
+
log_file: str | None = None,
|
|
188
190
|
):
|
|
189
191
|
self.force_passwd_prompt = force_passwd_prompt
|
|
190
192
|
self.never_passwd_prompt = never_passwd_prompt
|
|
@@ -208,6 +210,7 @@ class PGCli:
|
|
|
208
210
|
self.output_file = None
|
|
209
211
|
self.pgspecial = PGSpecial()
|
|
210
212
|
|
|
213
|
+
self.hide_named_query_text = "hide_named_query_text" in c["main"] and c["main"].as_bool("hide_named_query_text")
|
|
211
214
|
self.explain_mode = False
|
|
212
215
|
self.multi_line = c["main"].as_bool("multi_line")
|
|
213
216
|
self.multiline_mode = c["main"].get("multi_line_mode", "psql")
|
|
@@ -265,6 +268,8 @@ class PGCli:
|
|
|
265
268
|
|
|
266
269
|
self.query_history = []
|
|
267
270
|
|
|
271
|
+
self.auto_suggest = c["main"].as_bool("auto_suggest")
|
|
272
|
+
|
|
268
273
|
# Initialize completer
|
|
269
274
|
smart_completion = c["main"].as_bool("smart_completion")
|
|
270
275
|
keyword_casing = c["main"]["keyword_casing"]
|
|
@@ -307,7 +312,28 @@ class PGCli:
|
|
|
307
312
|
def quit(self):
|
|
308
313
|
raise PgCliQuitError
|
|
309
314
|
|
|
315
|
+
def toggle_named_query_quiet(self):
|
|
316
|
+
"""Toggle hiding of named query text"""
|
|
317
|
+
self.hide_named_query_text = not self.hide_named_query_text
|
|
318
|
+
status = "ON" if self.hide_named_query_text else "OFF"
|
|
319
|
+
message = f"Named query quiet mode: {status}"
|
|
320
|
+
return [(None, None, None, message)]
|
|
321
|
+
|
|
322
|
+
def _is_named_query_execution(self, text):
|
|
323
|
+
"""Check if the command is a named query execution (\n <name>)."""
|
|
324
|
+
text = text.strip()
|
|
325
|
+
return text.startswith("\\n ") and not text.startswith("\\ns ") and not text.startswith("\\nd ")
|
|
326
|
+
|
|
310
327
|
def register_special_commands(self):
|
|
328
|
+
self.pgspecial.register(
|
|
329
|
+
self.toggle_named_query_quiet,
|
|
330
|
+
"\\nq",
|
|
331
|
+
"\\nq",
|
|
332
|
+
"Toggle named query quiet mode (hide query text)",
|
|
333
|
+
arg_type=NO_QUERY,
|
|
334
|
+
case_sensitive=True,
|
|
335
|
+
)
|
|
336
|
+
|
|
311
337
|
self.pgspecial.register(
|
|
312
338
|
self.change_db,
|
|
313
339
|
"\\c",
|
|
@@ -610,7 +636,13 @@ class PGCli:
|
|
|
610
636
|
# If password prompt is not forced but no password is provided, try
|
|
611
637
|
# getting it from environment variable.
|
|
612
638
|
if not self.force_passwd_prompt and not passwd:
|
|
613
|
-
|
|
639
|
+
if dsn:
|
|
640
|
+
# Check if DSN contains a password - if so, don't use PGPASSWORD
|
|
641
|
+
parsed_dsn = conninfo_to_dict(dsn)
|
|
642
|
+
if "password" not in parsed_dsn:
|
|
643
|
+
passwd = os.environ.get("PGPASSWORD", "")
|
|
644
|
+
else:
|
|
645
|
+
passwd = os.environ.get("PGPASSWORD", "")
|
|
614
646
|
|
|
615
647
|
# Prompt for a password immediately if requested via the -W flag. This
|
|
616
648
|
# avoids wasting time trying to connect to the database and catching a
|
|
@@ -828,7 +860,14 @@ class PGCli:
|
|
|
828
860
|
if self.output_file and not text.startswith(("\\o ", "\\log-file", "\\? ", "\\echo ")):
|
|
829
861
|
try:
|
|
830
862
|
with open(self.output_file, "a", encoding="utf-8") as f:
|
|
831
|
-
|
|
863
|
+
should_hide = (
|
|
864
|
+
self.hide_named_query_text
|
|
865
|
+
and query.is_special
|
|
866
|
+
and query.successful
|
|
867
|
+
and self._is_named_query_execution(text)
|
|
868
|
+
)
|
|
869
|
+
if not should_hide:
|
|
870
|
+
click.echo(text, file=f)
|
|
832
871
|
click.echo("\n".join(output), file=f)
|
|
833
872
|
click.echo("", file=f) # extra newline
|
|
834
873
|
except OSError as e:
|
|
@@ -842,7 +881,14 @@ class PGCli:
|
|
|
842
881
|
try:
|
|
843
882
|
with open(self.log_file, "a", encoding="utf-8") as f:
|
|
844
883
|
click.echo(dt.datetime.now().isoformat(), file=f) # timestamp log
|
|
845
|
-
|
|
884
|
+
should_hide = (
|
|
885
|
+
self.hide_named_query_text
|
|
886
|
+
and query.is_special
|
|
887
|
+
and query.successful
|
|
888
|
+
and self._is_named_query_execution(text)
|
|
889
|
+
)
|
|
890
|
+
if not should_hide:
|
|
891
|
+
click.echo(text, file=f)
|
|
846
892
|
click.echo("\n".join(output), file=f)
|
|
847
893
|
click.echo("", file=f) # extra newline
|
|
848
894
|
except OSError as e:
|
|
@@ -889,7 +935,7 @@ class PGCli:
|
|
|
889
935
|
while 1:
|
|
890
936
|
try:
|
|
891
937
|
choice = click.prompt(
|
|
892
|
-
"A transaction is ongoing. Choose `c` to COMMIT, `r` to ROLLBACK, `a` to abort exit.",
|
|
938
|
+
"A transaction is ongoing. Choose `c` to COMMIT, `r` to ROLLBACK, `a` to abort exit, `force` to exit anyway.",
|
|
893
939
|
default="a",
|
|
894
940
|
)
|
|
895
941
|
except click.Abort:
|
|
@@ -901,6 +947,8 @@ class PGCli:
|
|
|
901
947
|
choice = choice.lower()
|
|
902
948
|
if choice == "a":
|
|
903
949
|
return False # do not quit
|
|
950
|
+
if choice == "force":
|
|
951
|
+
return True # quit anyway
|
|
904
952
|
if choice == "c":
|
|
905
953
|
query = self.execute_command("commit")
|
|
906
954
|
return query.successful # quit only if query is successful
|
|
@@ -922,7 +970,7 @@ class PGCli:
|
|
|
922
970
|
if not self.less_chatty:
|
|
923
971
|
print("Server: PostgreSQL", self.pgexecute.server_version)
|
|
924
972
|
print("Version:", __version__)
|
|
925
|
-
print("Home:
|
|
973
|
+
print("Home: https://pgcli.com")
|
|
926
974
|
|
|
927
975
|
try:
|
|
928
976
|
while True:
|
|
@@ -1033,7 +1081,7 @@ class PGCli:
|
|
|
1033
1081
|
# Render \t as 4 spaces instead of "^I"
|
|
1034
1082
|
TabsProcessor(char1=" ", char2=" "),
|
|
1035
1083
|
],
|
|
1036
|
-
auto_suggest=AutoSuggestFromHistory(),
|
|
1084
|
+
auto_suggest=AutoSuggestFromHistory() if self.auto_suggest else None,
|
|
1037
1085
|
tempfile_suffix=".sql",
|
|
1038
1086
|
# N.b. pgcli's multi-line mode controls submit-on-Enter (which
|
|
1039
1087
|
# overrides the default behaviour of prompt_toolkit) and is
|
|
@@ -1051,6 +1099,7 @@ class PGCli:
|
|
|
1051
1099
|
enable_suspend=True,
|
|
1052
1100
|
editing_mode=EditingMode.VI if self.vi_mode else EditingMode.EMACS,
|
|
1053
1101
|
search_ignore_case=True,
|
|
1102
|
+
cursor=ModalCursorShapeConfig(),
|
|
1054
1103
|
)
|
|
1055
1104
|
|
|
1056
1105
|
return prompt_app
|
|
@@ -1067,7 +1116,7 @@ class PGCli:
|
|
|
1067
1116
|
def _has_limit(self, sql):
|
|
1068
1117
|
if not sql:
|
|
1069
1118
|
return False
|
|
1070
|
-
return
|
|
1119
|
+
return any(token.match(sqlparse_tokens.Keyword, "LIMIT") for statement in sqlparse.parse(sql) for token in statement.flatten())
|
|
1071
1120
|
|
|
1072
1121
|
def _limit_output(self, cur):
|
|
1073
1122
|
limit = min(self.row_limit, cur.rowcount)
|
|
@@ -1136,6 +1185,18 @@ class PGCli:
|
|
|
1136
1185
|
style_output=self.style_output,
|
|
1137
1186
|
max_field_width=self.max_field_width,
|
|
1138
1187
|
)
|
|
1188
|
+
|
|
1189
|
+
# Hide query text for named queries in quiet mode
|
|
1190
|
+
if (
|
|
1191
|
+
self.hide_named_query_text
|
|
1192
|
+
and is_special
|
|
1193
|
+
and success
|
|
1194
|
+
and self._is_named_query_execution(text)
|
|
1195
|
+
and title
|
|
1196
|
+
and title.startswith("> ")
|
|
1197
|
+
):
|
|
1198
|
+
title = None
|
|
1199
|
+
|
|
1139
1200
|
execution = time() - start
|
|
1140
1201
|
formatted = format_output(title, cur, headers, status, settings, self.explain_mode)
|
|
1141
1202
|
|
|
@@ -1259,6 +1320,7 @@ class PGCli:
|
|
|
1259
1320
|
string = string.replace("\\i", str(self.pgexecute.pid) or "(none)")
|
|
1260
1321
|
string = string.replace("\\#", "#" if self.pgexecute.superuser else ">")
|
|
1261
1322
|
string = string.replace("\\n", "\n")
|
|
1323
|
+
string = string.replace("\\T", self.pgexecute.transaction_indicator)
|
|
1262
1324
|
return string
|
|
1263
1325
|
|
|
1264
1326
|
def get_last_query(self):
|
|
@@ -1589,7 +1651,7 @@ def cli(
|
|
|
1589
1651
|
|
|
1590
1652
|
if local_tz is None:
|
|
1591
1653
|
echo_error("No local time zone configuration found\n")
|
|
1592
|
-
|
|
1654
|
+
elif local_tz != server_tz:
|
|
1593
1655
|
click.secho(
|
|
1594
1656
|
f"Using local time zone {local_tz} (server uses {server_tz})",
|
|
1595
1657
|
fg="green",
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import sqlparse
|
|
1
2
|
from sqlparse import parse
|
|
2
3
|
from sqlparse.tokens import Keyword, CTE, DML
|
|
3
4
|
from sqlparse.sql import Identifier, IdentifierList, Parenthesis
|
|
4
5
|
from collections import namedtuple
|
|
5
6
|
from .meta import TableMetadata, ColumnMetadata
|
|
6
7
|
|
|
8
|
+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
|
|
9
|
+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
|
|
7
10
|
|
|
8
11
|
# TableExpression is a namedtuple representing a CTE, used internally
|
|
9
12
|
# name: cte alias assigned in the query
|
|
@@ -3,6 +3,9 @@ from collections import namedtuple
|
|
|
3
3
|
from sqlparse.sql import IdentifierList, Identifier, Function
|
|
4
4
|
from sqlparse.tokens import Keyword, DML, Punctuation
|
|
5
5
|
|
|
6
|
+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
|
|
7
|
+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
|
|
8
|
+
|
|
6
9
|
TableReference = namedtuple("TableReference", ["schema", "name", "alias", "is_function"])
|
|
7
10
|
TableReference.ref = property(
|
|
8
11
|
lambda self: self.alias or (self.name if self.name.islower() or self.name[0] == '"' else '"' + self.name + '"')
|
|
@@ -3,6 +3,9 @@ import sqlparse
|
|
|
3
3
|
from sqlparse.sql import Identifier
|
|
4
4
|
from sqlparse.tokens import Token, Error
|
|
5
5
|
|
|
6
|
+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
|
|
7
|
+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
|
|
8
|
+
|
|
6
9
|
cleanup_regex = {
|
|
7
10
|
# This matches only alphanumerics and underscores.
|
|
8
11
|
"alphanum_underscore": re.compile(r"(\w+)$"),
|
|
@@ -4,6 +4,8 @@ from sqlparse.tokens import Name
|
|
|
4
4
|
from collections import defaultdict
|
|
5
5
|
from .pgliterals.main import get_literals
|
|
6
6
|
|
|
7
|
+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
|
|
8
|
+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
|
|
7
9
|
|
|
8
10
|
white_space_regex = re.compile("\\s+", re.MULTILINE)
|
|
9
11
|
|