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 @@ -10,6 +10,10 @@ Features:
* Add the option to force-quit pgcli when a transaction is in progress.
* 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 @@ -181,6 +181,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 @@ -238,7 +239,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 +242 to +243

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@DiegoDAF Sigh. I know this is what I requested here, but I had a change of heart. Let's go back to aligning -t behavior with psql, to make it easier and less surprising for users coming from psql, and add back headers / timings suppression. Sorry about the back and forth.

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 @@ -1443,6 +1447,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 @@ -1506,6 +1518,7 @@ def cli(
row_limit,
application_name,
less_chatty,
tuples_only,
prompt,
prompt_dsn,
list_databases,
Expand Down Expand Up @@ -1568,6 +1581,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
Loading