Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Features:
Uses prompt_toolkit's ``ModalCursorShapeConfig``.
* Add support of Python 3.14.
* Drop support of Python 3.9.
* Add ``-t``/``--tuples-only`` CLI option to set table format at startup.
* Sets table format to ``csv-noheader`` (rows only, no headers)
* CLI shortcut equivalent to ``\T csv-noheader``
* Does not suppress timing or status messages (use ``\pset`` for that)

Bug fixes:
----------
Expand Down
16 changes: 15 additions & 1 deletion pgcli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ def __init__(
application_name="pgcli",
single_connection=False,
less_chatty=None,
tuples_only=None,
prompt=None,
prompt_dsn=None,
auto_vertical_output=False,
Expand Down Expand Up @@ -236,7 +237,10 @@ def __init__(

self.min_num_menu_lines = c["main"].as_int("min_num_menu_lines")
self.multiline_continuation_char = c["main"]["multiline_continuation_char"]
self.table_format = c["main"]["table_format"]
if tuples_only:
self.table_format = "csv-noheader"
Comment on lines +240 to +241

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor tuples-only by suppressing extra output

When -t/--tuples-only is used, this branch only switches the formatter to csv-noheader; the output path still appends command status in format_output (pgcli/main.py:1968-1970) and prints timing whenever enabled (pgcli/main.py:901, with the default config setting timing = True). PostgreSQL documents --tuples-only as turning off column names and row-count footers, so with the default config a query still emits SELECT n and Time: ..., which breaks users relying on this option for rows-only output. Please carry the tuples-only state through to suppress status/timing, or avoid exposing it as psql-style tuples-only.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @codex, fair point on the psql semantics. This is intentional for this PR: -t was deliberately narrowed during review to be a startup shortcut for the output format only (like a CLI \T), not psql's full tuples-only. Suppressing the status footer and timing is kept to separate, composable flags (--no-timings / --no-status) rather than bundled into -t, so each concern stays granular. So the scope here is "format shortcut", and rows-only-with-no-footers is achieved by combining flags. Open to revisiting the naming if -t/--tuples-only reads as too psql-flavored for that scope.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else:
self.table_format = c["main"]["table_format"]
self.syntax_style = c["main"]["syntax_style"]
self.cli_style = c["colors"]
self.wider_completion_menu = c["main"].as_bool("wider_completion_menu")
Expand Down Expand Up @@ -1439,6 +1443,14 @@ def echo_via_pager(self, text, color=None):
default=False,
help="Skip intro on startup and goodbye on exit.",
)
@click.option(
"-t",
"--tuples-only",
"tuples_only",
is_flag=True,
default=False,
help="Print rows only, using csv-noheader format. Same as \\T csv-noheader.",
)
@click.option("--prompt", help='Prompt format (Default: "\\u@\\h:\\d> ").')
@click.option(
"--prompt-dsn",
Expand Down Expand Up @@ -1502,6 +1514,7 @@ def cli(
row_limit,
application_name,
less_chatty,
tuples_only,
prompt,
prompt_dsn,
list_databases,
Expand Down Expand Up @@ -1564,6 +1577,7 @@ def cli(
application_name=application_name,
single_connection=single_connection,
less_chatty=less_chatty,
tuples_only=tuples_only,
prompt=prompt,
prompt_dsn=prompt_dsn,
auto_vertical_output=auto_vertical_output,
Expand Down
44 changes: 44 additions & 0 deletions tests/test_tuples_only.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from unittest.mock import patch

from click.testing import CliRunner

from pgcli.main import cli, PGCli


def test_tuples_only_flag_passed_to_pgcli():
"""Test that -t passes tuples_only=True to PGCli."""
runner = CliRunner()
with patch.object(PGCli, "__init__", autospec=True, return_value=None) as mock_pgcli:
runner.invoke(cli, ["-t", "mydb"])
call_kwargs = mock_pgcli.call_args[1]
assert call_kwargs["tuples_only"] is True


def test_tuples_only_long_form():
"""Test that --tuples-only passes tuples_only=True to PGCli."""
runner = CliRunner()
with patch.object(PGCli, "__init__", autospec=True, return_value=None) as mock_pgcli:
runner.invoke(cli, ["--tuples-only", "mydb"])
call_kwargs = mock_pgcli.call_args[1]
assert call_kwargs["tuples_only"] is True


def test_tuples_only_not_set_by_default():
"""Test that tuples_only is False when -t is not used."""
runner = CliRunner()
with patch.object(PGCli, "__init__", autospec=True, return_value=None) as mock_pgcli:
runner.invoke(cli, ["mydb"])
call_kwargs = mock_pgcli.call_args[1]
assert call_kwargs["tuples_only"] is False


def test_tuples_only_sets_csv_noheader_format():
"""Test that tuples_only=True sets table_format to csv-noheader."""
pgcli = PGCli(tuples_only=True)
assert pgcli.table_format == "csv-noheader"


def test_default_table_format_without_tuples_only():
"""Test that table_format uses config default when tuples_only is False."""
pgcli = PGCli()
assert pgcli.table_format != "csv-noheader" # Uses config default