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.
Files changed (128) hide show
  1. {pgcli-4.4.0 → pgcli-4.5.0}/.github/PULL_REQUEST_TEMPLATE.md +1 -2
  2. {pgcli-4.4.0 → pgcli-4.5.0}/.github/workflows/ci.yml +1 -1
  3. pgcli-4.5.0/.github/workflows/codex-review.yml +91 -0
  4. {pgcli-4.4.0 → pgcli-4.5.0}/.github/workflows/publish.yml +3 -3
  5. {pgcli-4.4.0 → pgcli-4.5.0}/.pre-commit-config.yaml +1 -1
  6. {pgcli-4.4.0 → pgcli-4.5.0}/AUTHORS +4 -0
  7. {pgcli-4.4.0 → pgcli-4.5.0}/CONTRIBUTING.rst +0 -37
  8. {pgcli-4.4.0 → pgcli-4.5.0}/Dockerfile +1 -1
  9. {pgcli-4.4.0 → pgcli-4.5.0}/PKG-INFO +11 -11
  10. {pgcli-4.4.0 → pgcli-4.5.0}/README.rst +7 -6
  11. {pgcli-4.4.0 → pgcli-4.5.0}/changelog.rst +24 -1
  12. pgcli-4.5.0/pgcli/__init__.py +1 -0
  13. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/magic.py +1 -1
  14. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/main.py +73 -11
  15. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/__init__.py +2 -0
  16. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/ctes.py +3 -0
  17. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/tables.py +3 -0
  18. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/utils.py +3 -0
  19. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/pgliterals/pgliterals.json +1 -0
  20. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/prioritization.py +2 -0
  21. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/sqlcompletion.py +3 -1
  22. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgbuffer.py +6 -2
  23. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgclirc +9 -0
  24. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgexecute.py +23 -3
  25. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/PKG-INFO +11 -11
  26. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/SOURCES.txt +2 -1
  27. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/requires.txt +1 -1
  28. {pgcli-4.4.0 → pgcli-4.5.0}/pyproject.toml +5 -4
  29. {pgcli-4.4.0 → pgcli-4.5.0}/tests/conftest.py +8 -0
  30. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_main.py +41 -0
  31. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_naive_completion.py +7 -0
  32. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_pgexecute.py +9 -9
  33. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_rowlimit.py +26 -2
  34. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_smart_completion_public_schema_only.py +6 -0
  35. pgcli-4.5.0/tests/test_trailing_comments.py +47 -0
  36. {pgcli-4.4.0 → pgcli-4.5.0}/tox.ini +11 -0
  37. pgcli-4.4.0/Vagrantfile +0 -138
  38. pgcli-4.4.0/pgcli/__init__.py +0 -1
  39. {pgcli-4.4.0 → pgcli-4.5.0}/.coveragerc +0 -0
  40. {pgcli-4.4.0 → pgcli-4.5.0}/.editorconfig +0 -0
  41. {pgcli-4.4.0 → pgcli-4.5.0}/.git-blame-ignore-revs +0 -0
  42. {pgcli-4.4.0 → pgcli-4.5.0}/.github/ISSUE_TEMPLATE.md +0 -0
  43. {pgcli-4.4.0 → pgcli-4.5.0}/.github/workflows/codeql.yml +0 -0
  44. {pgcli-4.4.0 → pgcli-4.5.0}/.gitignore +0 -0
  45. {pgcli-4.4.0 → pgcli-4.5.0}/LICENSE.txt +0 -0
  46. {pgcli-4.4.0 → pgcli-4.5.0}/MANIFEST.in +0 -0
  47. {pgcli-4.4.0 → pgcli-4.5.0}/RELEASES.md +0 -0
  48. {pgcli-4.4.0 → pgcli-4.5.0}/TODO +0 -0
  49. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/__main__.py +0 -0
  50. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/auth.py +0 -0
  51. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/completion_refresher.py +0 -0
  52. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/config.py +0 -0
  53. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/explain_output_formatter.py +0 -0
  54. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/key_bindings.py +0 -0
  55. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/__init__.py +0 -0
  56. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/formatter/__init__.py +0 -0
  57. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/formatter/sqlformatter.py +0 -0
  58. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/parseutils/meta.py +0 -0
  59. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/pgliterals/__init__.py +0 -0
  60. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/pgliterals/main.py +0 -0
  61. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/packages/prompt_utils.py +0 -0
  62. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgcompleter.py +0 -0
  63. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgstyle.py +0 -0
  64. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pgtoolbar.py +0 -0
  65. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli/pyev.py +0 -0
  66. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli-completion.bash +0 -0
  67. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/dependency_links.txt +0 -0
  68. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/entry_points.txt +0 -0
  69. {pgcli-4.4.0 → pgcli-4.5.0}/pgcli.egg-info/top_level.txt +0 -0
  70. {pgcli-4.4.0 → pgcli-4.5.0}/post-install +0 -0
  71. {pgcli-4.4.0 → pgcli-4.5.0}/post-remove +0 -0
  72. {pgcli-4.4.0 → pgcli-4.5.0}/pylintrc +0 -0
  73. {pgcli-4.4.0 → pgcli-4.5.0}/release.py +0 -0
  74. {pgcli-4.4.0 → pgcli-4.5.0}/sanity_checks.txt +0 -0
  75. {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/image01.png +0 -0
  76. {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/image02.png +0 -0
  77. {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/kharkiv-destroyed.jpg +0 -0
  78. {pgcli-4.4.0 → pgcli-4.5.0}/screenshots/pgcli.gif +0 -0
  79. {pgcli-4.4.0 → pgcli-4.5.0}/setup.cfg +0 -0
  80. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/__init__.py +0 -0
  81. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/auto_vertical.feature +0 -0
  82. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/basic_commands.feature +0 -0
  83. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/crud_database.feature +0 -0
  84. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/crud_table.feature +0 -0
  85. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/db_utils.py +0 -0
  86. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/environment.py +0 -0
  87. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/expanded.feature +0 -0
  88. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_data/help.txt +0 -0
  89. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_data/help_commands.txt +0 -0
  90. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_data/mock_pg_service.conf +0 -0
  91. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/fixture_utils.py +0 -0
  92. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/iocommands.feature +0 -0
  93. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/named_queries.feature +0 -0
  94. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/pgbouncer.feature +0 -0
  95. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/specials.feature +0 -0
  96. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/__init__.py +0 -0
  97. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/auto_vertical.py +0 -0
  98. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/basic_commands.py +0 -0
  99. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/crud_database.py +0 -0
  100. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/crud_table.py +0 -0
  101. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/expanded.py +0 -0
  102. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/iocommands.py +0 -0
  103. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/named_queries.py +0 -0
  104. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/pgbouncer.py +0 -0
  105. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/specials.py +0 -0
  106. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/steps/wrappers.py +0 -0
  107. {pgcli-4.4.0 → pgcli-4.5.0}/tests/features/wrappager.py +0 -0
  108. {pgcli-4.4.0 → pgcli-4.5.0}/tests/formatter/__init__.py +0 -0
  109. {pgcli-4.4.0 → pgcli-4.5.0}/tests/formatter/test_sqlformatter.py +0 -0
  110. {pgcli-4.4.0 → pgcli-4.5.0}/tests/metadata.py +0 -0
  111. {pgcli-4.4.0 → pgcli-4.5.0}/tests/parseutils/test_ctes.py +0 -0
  112. {pgcli-4.4.0 → pgcli-4.5.0}/tests/parseutils/test_function_metadata.py +0 -0
  113. {pgcli-4.4.0 → pgcli-4.5.0}/tests/parseutils/test_parseutils.py +0 -0
  114. {pgcli-4.4.0 → pgcli-4.5.0}/tests/pytest.ini +0 -0
  115. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_application_name.py +0 -0
  116. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_auth.py +0 -0
  117. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_completion_refresher.py +0 -0
  118. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_config.py +0 -0
  119. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_fuzzy_completion.py +0 -0
  120. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_init_commands_simple.py +0 -0
  121. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_pgcompleter.py +0 -0
  122. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_pgspecial.py +0 -0
  123. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_prioritization.py +0 -0
  124. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_prompt_utils.py +0 -0
  125. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_smart_completion_multiple_schemata.py +0 -0
  126. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_sqlcompletion.py +0 -0
  127. {pgcli-4.4.0 → pgcli-4.5.0}/tests/test_ssh_tunnel.py +0 -0
  128. {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)
@@ -14,7 +14,7 @@ jobs:
14
14
 
15
15
  strategy:
16
16
  matrix:
17
- python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
17
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
18
18
 
19
19
  services:
20
20
  postgres:
@@ -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.9", "3.10", "3.11", "3.12", "3.13"]
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.13
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
@@ -1,7 +1,7 @@
1
1
  repos:
2
2
  - repo: https://github.com/astral-sh/ruff-pre-commit
3
3
  # Ruff version.
4
- rev: v0.11.7
4
+ rev: v0.14.10
5
5
  hooks:
6
6
  # Run the linter.
7
7
  - id: ruff
@@ -144,6 +144,10 @@ Contributors:
144
144
  * Jay Knight (jay-knight)
145
145
  * fbdb
146
146
  * Charbel Jacquin (charbeljc)
147
+ * Devadathan M B (devadathanmb)
148
+ * Charalampos Stratakis
149
+ * Laszlo Bimba (bimlas)
150
+ * Anjanna
147
151
 
148
152
  Creator:
149
153
  --------
@@ -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,4 +1,4 @@
1
- FROM python:3.8
1
+ FROM python:3.14
2
2
 
3
3
  COPY . /app
4
4
  RUN cd /app && pip install -e .
@@ -1,31 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pgcli
3
- Version: 4.4.0
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.9
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<8.1.8,>=4.1
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: http://pgcli.com
84
+ Home Page: https://pgcli.com
86
85
 
87
- MySQL Equivalent: http://mycli.net
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 <http://twitter.com/amjithr>`_
217
- * Irina, pgcli maintainer: i.chernyavska@gmail.com, Twitter: `@irinatruong <http://twitter.com/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 <http://github.com/jonathanslenders/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 <http://click.pocoo.org/>`_ is used for command line option parsing
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: http://pgcli.com
31
+ Home Page: https://pgcli.com
32
32
 
33
- MySQL Equivalent: http://mycli.net
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 <http://twitter.com/amjithr>`_
163
- * Irina, pgcli maintainer: i.chernyavska@gmail.com, Twitter: `@irinatruong <http://twitter.com/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 <http://github.com/jonathanslenders/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 <http://click.pocoo.org/>`_ is used for command line option parsing
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.session.engine.url
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: Optional[str] = None,
187
- log_file: Optional[str] = None,
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
- passwd = os.environ.get("PGPASSWORD", "")
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
- click.echo(text, file=f)
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
- click.echo(text, file=f)
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: http://pgcli.com")
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 "limit " in sql.lower()
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
- else:
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,5 +1,7 @@
1
1
  import sqlparse
2
2
 
3
+ sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
4
+ sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
3
5
 
4
6
  BASE_KEYWORDS = [
5
7
  "drop",
@@ -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+)$"),
@@ -439,6 +439,7 @@
439
439
  "TRUNC",
440
440
  "UNNEST",
441
441
  "UPPER",
442
+ "VERSION",
442
443
  "WIDTH",
443
444
  "WIDTH_BUCKET",
444
445
  "XMLAGG"
@@ -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