From 94a6393c28547e323737d97f0a22c801fcd96b6f Mon Sep 17 00:00:00 2001 From: ParamTanna Date: Thu, 5 Nov 2026 16:22:41 +0000 Subject: [PATCH] Fix NoneType crash in FunctionMetadata.fields() for unnamed args (#1204) fields() guarded only arg_modes, but a function argument can arrive with no name (an unnamed variadic arg), so arg_names is None while arg_modes is set, and the zip() raised NoneType is not iterable during completion. args() right above already guards arg_names; fields() now guards it too, falling back to the function-name output column as the no-output-parameters branch does. --- pgcli/packages/parseutils/meta.py | 9 ++++++--- tests/parseutils/test_function_metadata.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pgcli/packages/parseutils/meta.py b/pgcli/packages/parseutils/meta.py index df41cf4ee..8879511ed 100644 --- a/pgcli/packages/parseutils/meta.py +++ b/pgcli/packages/parseutils/meta.py @@ -151,10 +151,13 @@ def fields(self): if self.return_type.lower() == "void": return [] - elif not self.arg_modes: - # For functions without output parameters, the function name - # is used as the name of the output column. + elif not self.arg_modes or not self.arg_names or not self.arg_types: + # For functions without output parameters, the function name is + # used as the name of the output column. # E.g. 'SELECT unnest FROM unnest(...);' + # arg_names/arg_types can also be None (e.g. an unnamed variadic + # argument), which args() already guards against; without this the + # zip() below raised 'NoneType is not iterable'. return [ColumnMetadata(self.func_name, self.return_type, [])] return [ diff --git a/tests/parseutils/test_function_metadata.py b/tests/parseutils/test_function_metadata.py index c4000ab1c..2943d975d 100644 --- a/tests/parseutils/test_function_metadata.py +++ b/tests/parseutils/test_function_metadata.py @@ -11,3 +11,14 @@ 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_unnamed_variadic_arg(): + # A variadic argument can arrive with no name, so arg_names is None while + # arg_modes is set. fields() used to raise 'NoneType is not iterable' on + # the zip() (issue #1204); it now falls back to the function-name column. + f = FunctionMetadata( + 'public', 'labels', None, ['text[]'], ['v'], 'hstore', + False, False, False, False, None, + ) + assert [(c.name, c.datatype) for c in f.fields()] == [('labels', 'hstore')] \ No newline at end of file