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
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ Contributors:
* Charalampos Stratakis
* Laszlo Bimba (bimlas)
* Anjanna
* Diego

Creator:
--------
Expand Down
10 changes: 10 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
Upcoming
========

Features:
---------
* Add ``\\ne <name>`` to edit a named query in the external editor. Loads the
named query's SQL into ``$EDITOR``; on save it is written back to the
``[named queries]`` section, creating it if it does not exist. Complements
``\\ns`` (save) by making longer queries easier to edit ([issue 1430](https://github.com/dbcli/pgcli/issues/1430)).

4.5.0 (2026-06-02)
==================

Expand Down
32 changes: 32 additions & 0 deletions pgcli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,31 @@ def toggle_named_query_quiet(self):
message = f"Named query quiet mode: {status}"
return [(None, None, None, message)]

def edit_named_query(self, pattern, **_):
r"""Edit (or create) a named query in the external editor (\ne name).

Loads the named query's SQL into ``$EDITOR``; on save, persists it back
to the ``[named queries]`` section. If the name does not exist, the
editor opens empty and saving creates it.
"""
name = pattern.strip()
if not name:
return [(None, None, None, "Usage: \\ne <name>")]

existing = NamedQueries.instance.get(name)
sql, message = special.open_external_editor(sql=existing or "")
if message:
return [(None, None, None, message)]

sql = (sql or "").strip()
if not sql:
return [(None, None, None, f"{name}: empty query, not saved.")]
if existing is not None and sql == existing.strip():
return [(None, None, None, f"{name}: no changes.")]

NamedQueries.instance.save(name, sql)
return [(None, None, None, f"{name}: {'Created' if existing is None else 'Saved'}")]

def _is_named_query_execution(self, text):
"""Check if the command is a named query execution (\n <name>)."""
text = text.strip()
Expand All @@ -334,6 +359,13 @@ def register_special_commands(self):
case_sensitive=True,
)

self.pgspecial.register(
self.edit_named_query,
"\\ne",
"\\ne name",
"Edit a named query in the external editor.",
)

self.pgspecial.register(
self.change_db,
"\\c",
Expand Down
45 changes: 45 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,48 @@ def test_notifications(executor):
with mock.patch("pgcli.main.click.secho") as mock_secho:
run(executor, "notify chan1, 'testing2'")
mock_secho.assert_not_called()


def test_edit_named_query():
"""Test \\ne edits/creates a named query via the external editor."""
from pgspecial.namedqueries import NamedQueries

with tempfile.TemporaryDirectory() as tmpdir:
config_file = os.path.join(tmpdir, "config")
with open(config_file, "w") as f:
f.write("[main]\n")
f.write(f"log_file = {os.path.join(tmpdir, 'pgcli.log')}\n")

cli = PGCli(pgclirc_file=config_file)

# Create a new named query (editor returns SQL, no error).
with mock.patch("pgcli.main.special.open_external_editor", return_value=("select 1", None)):
out = cli.edit_named_query("foo")
assert "Created" in out[0][3]
assert NamedQueries.instance.get("foo") == "select 1"

# Update the existing one.
with mock.patch("pgcli.main.special.open_external_editor", return_value=("select 2", None)):
out = cli.edit_named_query("foo")
assert "Saved" in out[0][3]
assert NamedQueries.instance.get("foo") == "select 2"

# No changes -> not re-saved.
with mock.patch("pgcli.main.special.open_external_editor", return_value=("select 2", None)):
out = cli.edit_named_query("foo")
assert "no changes" in out[0][3]

# Empty editor result -> not saved, previous value kept.
with mock.patch("pgcli.main.special.open_external_editor", return_value=("", None)):
out = cli.edit_named_query("foo")
assert "not saved" in out[0][3]
assert NamedQueries.instance.get("foo") == "select 2"

# Editor reported an error -> surfaced, nothing saved.
with mock.patch("pgcli.main.special.open_external_editor", return_value=(None, "boom")):
out = cli.edit_named_query("foo")
assert out[0][3] == "boom"

# Missing name -> usage message.
out = cli.edit_named_query("")
assert "Usage" in out[0][3]
Loading