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 @@ -158,6 +158,7 @@ Contributors:
* VXNCXNX

* jackwalkerlabs (Codex-assisted)
* youdie006

Creator:
--------
Expand Down
4 changes: 4 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Features:
* Add a ``\service`` prompt token for connections made using ``service=...``
or ``PGSERVICE``. For example, set ``prompt = '\service> '``.

Bug fixes:
----------
* Fix ``NoneType`` crash when completing functions with unnamed (variadic) arguments (#1204).

4.7.1 (2026-09-20)
==================

Expand Down
4 changes: 2 additions & 2 deletions pgcli/packages/parseutils/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,14 @@ def fields(self):

if self.return_type.lower() == "void":
return []
elif not self.arg_modes:
elif not any(mode in ("o", "b", "t") for mode in self.arg_modes or []):
# For functions without output parameters, the function name
# is used as the name of the output column.
# E.g. 'SELECT unnest FROM unnest(...);'
return [ColumnMetadata(self.func_name, self.return_type, [])]

return [
ColumnMetadata(name, typ, [])
for name, typ, mode in zip(self.arg_names, self.arg_types, self.arg_modes)
for name, typ, mode in zip(self.arg_names or [], self.arg_types or [], self.arg_modes)

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 Return the scalar field for variadic-only functions

When proargmodes is populated only because the function has input/variadic parameters, this now zips an empty arg_names list and returns []. That avoids the crash, but it also means populate_scoped_cols() gets no column metadata for functions such as labels(variadic text[]) RETURNS hstore, even though the existing comment above says functions without output parameters should expose the function name as the output column. In completions for FROM labels(...) l, this silently drops the labels column instead of falling back to ColumnMetadata(self.func_name, self.return_type, []) when no OUT/INOUT/TABLE modes are present.

Useful? React with 👍 / 👎.

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.

@youdie006 This seems valid, wdyt?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, it was right: with only a VARIADIC arg, fields() returned [] and the labels column was lost. f88324d falls back to the function name whenever there are no OUT/INOUT/TABLE modes, and the test now expects [ColumnMetadata("labels", "hstore", [])]. I also rebased onto main to resolve the AUTHORS/changelog conflicts. Sorry for the long delay.

if mode in ("o", "b", "t")
] # OUT, INOUT, TABLE
21 changes: 20 additions & 1 deletion tests/parseutils/test_function_metadata.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pgcli.packages.parseutils.meta import FunctionMetadata
from pgcli.packages.parseutils.meta import ColumnMetadata, FunctionMetadata


def test_function_metadata_eq():
Expand All @@ -11,3 +11,22 @@ def test_function_metadata_eq():
assert not (f1 == f3)
assert hash(f1) == hash(f2)
assert hash(f1) != hash(f3)


def test_function_metadata_fields_with_variadic_and_no_arg_names():
# Issue #1204: labels(variadic text[]) RETURNS hstore has arg_modes but no arg_names.
f = FunctionMetadata(
"public",
"labels",
None, # arg_names: variadic arg has no name
["text[]"],
["v"], # VARIADIC
"hstore",
False,
False,
False,
False,
None,
)
# No OUT/INOUT/TABLE columns, so the function name is the output column.
assert f.fields() == [ColumnMetadata("labels", "hstore", [])]