Skip to content

Commit 6d2eace

Browse files
committed
Merge remote-tracking branch 'original/main' into feature/tuples-only-option
2 parents fe47a48 + 7da5cd2 commit 6d2eace

20 files changed

Lines changed: 248 additions & 28 deletions

AUTHORS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,13 @@ Contributors:
144144
* Jay Knight (jay-knight)
145145
* fbdb
146146
* Charbel Jacquin (charbeljc)
147+
* Jeronimo Garcia (bechampion)
147148
* Devadathan M B (devadathanmb)
148149
* Charalampos Stratakis
149150
* Laszlo Bimba (bimlas)
151+
* Anjanna
152+
* Shayan Golshani (shgol)
153+
* Tommi Kyntölä (kynde)
150154

151155
Creator:
152156
--------

changelog.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
Upcoming (TBD)
2-
==============
1+
4.5.0 (2026-06-02)
2+
==================
33

44
Features:
55
---------
66
* Add support for `\\T` prompt escape sequence to display transaction status (similar to psql's `%x`).
77
* Add cursor shape support for vi mode. When ``vi = True``, the terminal cursor now
88
reflects the current editing mode: beam in INSERT, block in NORMAL, underline in REPLACE.
99
Uses prompt_toolkit's ``ModalCursorShapeConfig``.
10+
* Add the option to force-quit pgcli when a transaction is in progress.
1011
* Add support of Python 3.14.
1112
* Drop support of Python 3.9.
1213
* Add ``-t``/``--tuples-only`` CLI option to set table format at startup.
@@ -18,6 +19,13 @@ Bug fixes:
1819
----------
1920
* Add `VERSION` to built-in function completion so `SELECT VERSION();` is suggested.
2021
* Hide timezone notice at startup when local and server timezones are the same.
22+
* Let `sqlparse` accept arbitrarily-large queries.
23+
* Respect user-specified `LIMIT` clauses when the limit value starts on a new line.
24+
* Fix trailing SQL comments preventing query submission and execution.
25+
* ``SELECT 1; -- note`` now submits correctly in multiline mode
26+
* ``rstrip(";")`` in ``pgexecute.py`` now handles comments after the semicolon
27+
* Fix completion crash when tables are created during refresh.
28+
* Suggest columns after `GROUP BY`, like `ORDER BY` already does.
2129

2230
4.4.0 (2025-12-24)
2331
==================

pgcli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "4.4.0"
1+
__version__ = "4.5.0"

pgcli/main.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
from cli_helpers.utils import strip_ansi
2828
from .explain_output_formatter import ExplainOutputFormatter
2929
import click
30+
import sqlparse
31+
from sqlparse import tokens as sqlparse_tokens
3032
import tzlocal
3133

3234
try:
@@ -654,7 +656,7 @@ def connect(self, database="", host="", user="", port="", passwd="", dsn="", **k
654656
if self.force_passwd_prompt and not passwd:
655657
passwd = click.prompt("Password for %s" % user, hide_input=True, show_default=False, type=str)
656658

657-
key = f"{user}@{host}"
659+
key = f"{user}@{host}@{port}"
658660

659661
if not passwd and auth.keyring:
660662
passwd = auth.keyring_get_password(key)
@@ -937,7 +939,7 @@ def _check_ongoing_transaction_and_allow_quitting(self):
937939
while 1:
938940
try:
939941
choice = click.prompt(
940-
"A transaction is ongoing. Choose `c` to COMMIT, `r` to ROLLBACK, `a` to abort exit.",
942+
"A transaction is ongoing. Choose `c` to COMMIT, `r` to ROLLBACK, `a` to abort exit, `force` to exit anyway.",
941943
default="a",
942944
)
943945
except click.Abort:
@@ -949,6 +951,8 @@ def _check_ongoing_transaction_and_allow_quitting(self):
949951
choice = choice.lower()
950952
if choice == "a":
951953
return False # do not quit
954+
if choice == "force":
955+
return True # quit anyway
952956
if choice == "c":
953957
query = self.execute_command("commit")
954958
return query.successful # quit only if query is successful
@@ -1116,7 +1120,7 @@ def _should_limit_output(self, sql, cur):
11161120
def _has_limit(self, sql):
11171121
if not sql:
11181122
return False
1119-
return "limit " in sql.lower()
1123+
return any(token.match(sqlparse_tokens.Keyword, "LIMIT") for statement in sqlparse.parse(sql) for token in statement.flatten())
11201124

11211125
def _limit_output(self, cur):
11221126
limit = min(self.row_limit, cur.rowcount)

pgcli/packages/parseutils/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import sqlparse
22

3+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
4+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
35

46
BASE_KEYWORDS = [
57
"drop",

pgcli/packages/parseutils/ctes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import sqlparse
12
from sqlparse import parse
23
from sqlparse.tokens import Keyword, CTE, DML
34
from sqlparse.sql import Identifier, IdentifierList, Parenthesis
45
from collections import namedtuple
56
from .meta import TableMetadata, ColumnMetadata
67

8+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
9+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
710

811
# TableExpression is a namedtuple representing a CTE, used internally
912
# name: cte alias assigned in the query

pgcli/packages/parseutils/tables.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from sqlparse.sql import IdentifierList, Identifier, Function
44
from sqlparse.tokens import Keyword, DML, Punctuation
55

6+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
7+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
8+
69
TableReference = namedtuple("TableReference", ["schema", "name", "alias", "is_function"])
710
TableReference.ref = property(
811
lambda self: self.alias or (self.name if self.name.islower() or self.name[0] == '"' else '"' + self.name + '"')

pgcli/packages/parseutils/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from sqlparse.sql import Identifier
44
from sqlparse.tokens import Token, Error
55

6+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
7+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
8+
69
cleanup_regex = {
710
# This matches only alphanumerics and underscores.
811
"alphanum_underscore": re.compile(r"(\w+)$"),

pgcli/packages/prioritization.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from collections import defaultdict
55
from .pgliterals.main import get_literals
66

7+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
8+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
79

810
white_space_regex = re.compile("\\s+", re.MULTILINE)
911

pgcli/packages/sqlcompletion.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from .parseutils.ctes import isolate_query_ctes
88
from pgspecial.main import parse_special_command
99

10+
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None
11+
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None
1012

1113
Special = namedtuple("Special", [])
1214
Database = namedtuple("Database", [])
@@ -381,7 +383,7 @@ def suggest_based_on_last_token(token, stmt):
381383
# E.g. 'UPDATE foo SET'
382384
return (Column(table_refs=stmt.get_tables(), local_tables=stmt.local_tables),)
383385

384-
elif token_v in ("select", "where", "having", "order by", "distinct"):
386+
elif token_v in ("select", "where", "having", "group by", "order by", "distinct"):
385387
return _suggest_expression(token_v, stmt)
386388
elif token_v == "as":
387389
# Don't suggest anything for aliases

0 commit comments

Comments
 (0)