From bb5aff99e511c6ed4d641e03fe3b96482c020b8b Mon Sep 17 00:00:00 2001 From: youdie006 Date: Sat, 20 Jun 2026 22:40:28 +0900 Subject: [PATCH 1/2] Fix NoneType crash completing functions with unnamed arguments FunctionMetadata.fields() called zip() over self.arg_names, which is None for functions with unnamed (variadic) arguments such as hstore's labels(variadic text[]), raising "'NoneType' object is not iterable" in get_completions(). Guard the zip with 'or []' (mirroring args()). Adds a regression test plus changelog and AUTHORS entries. Closes #1204 --- AUTHORS | 1 + changelog.rst | 4 ++++ pgcli/packages/parseutils/meta.py | 2 +- tests/parseutils/test_function_metadata.py | 22 ++++++++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 92045428c..c5a2fdec7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -158,6 +158,7 @@ Contributors: * VXNCXNX * jackwalkerlabs (Codex-assisted) + * youdie006 Creator: -------- diff --git a/changelog.rst b/changelog.rst index 81fbefe78..2f9e32872 100644 --- a/changelog.rst +++ b/changelog.rst @@ -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) ================== diff --git a/pgcli/packages/parseutils/meta.py b/pgcli/packages/parseutils/meta.py index df41cf4ee..ca96fd8ea 100644 --- a/pgcli/packages/parseutils/meta.py +++ b/pgcli/packages/parseutils/meta.py @@ -159,6 +159,6 @@ def fields(self): 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) if mode in ("o", "b", "t") ] # OUT, INOUT, TABLE diff --git a/tests/parseutils/test_function_metadata.py b/tests/parseutils/test_function_metadata.py index c4000ab1c..05fd4bf5e 100644 --- a/tests/parseutils/test_function_metadata.py +++ b/tests/parseutils/test_function_metadata.py @@ -11,3 +11,25 @@ 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(): + # Regression for issue #1204: a function with a VARIADIC argument that has + # a type but no name (e.g. `labels(variadic text[]) RETURNS hstore`) leaves + # arg_names as None while arg_modes is truthy. fields() must not raise + # "'NoneType' object is not iterable". + 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 fields() yields nothing. + assert f.fields() == [] From f88324df5d0fd0e321432f6d51f5f611c2241705 Mon Sep 17 00:00:00 2001 From: youdie006 Date: Mon, 19 Oct 2026 09:13:02 +0900 Subject: [PATCH 2/2] Use the function name as the output column when no OUT/INOUT/TABLE args exist --- pgcli/packages/parseutils/meta.py | 2 +- tests/parseutils/test_function_metadata.py | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pgcli/packages/parseutils/meta.py b/pgcli/packages/parseutils/meta.py index ca96fd8ea..4eded3d79 100644 --- a/pgcli/packages/parseutils/meta.py +++ b/pgcli/packages/parseutils/meta.py @@ -151,7 +151,7 @@ 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(...);' diff --git a/tests/parseutils/test_function_metadata.py b/tests/parseutils/test_function_metadata.py index 05fd4bf5e..f6ddb6f2f 100644 --- a/tests/parseutils/test_function_metadata.py +++ b/tests/parseutils/test_function_metadata.py @@ -1,4 +1,4 @@ -from pgcli.packages.parseutils.meta import FunctionMetadata +from pgcli.packages.parseutils.meta import ColumnMetadata, FunctionMetadata def test_function_metadata_eq(): @@ -14,10 +14,7 @@ def test_function_metadata_eq(): def test_function_metadata_fields_with_variadic_and_no_arg_names(): - # Regression for issue #1204: a function with a VARIADIC argument that has - # a type but no name (e.g. `labels(variadic text[]) RETURNS hstore`) leaves - # arg_names as None while arg_modes is truthy. fields() must not raise - # "'NoneType' object is not iterable". + # Issue #1204: labels(variadic text[]) RETURNS hstore has arg_modes but no arg_names. f = FunctionMetadata( "public", "labels", @@ -31,5 +28,5 @@ def test_function_metadata_fields_with_variadic_and_no_arg_names(): False, None, ) - # No OUT/INOUT/TABLE columns, so fields() yields nothing. - assert f.fields() == [] + # No OUT/INOUT/TABLE columns, so the function name is the output column. + assert f.fields() == [ColumnMetadata("labels", "hstore", [])]