Skip to content

Commit 2224ee0

Browse files
Use the function name when fields() has no named output columns
DiegoDAF pointed out that the earlier change only moved the crash: for a TABLE(...) function declared without argument names, fields() now returned ColumnMetadata entries with name=None, and generate_alias() then failed iterating that None. When arg_modes has no usable output column (unnamed TABLE columns, or only a variadic parameter), fall back to the function name as the column name, like the branch above already does for functions without output parameters. The variadic case now completes 'labels' instead of offering nothing, and the TABLE case no longer reaches generate_alias() with None.
1 parent 4979496 commit 2224ee0

2 files changed

Lines changed: 23 additions & 13 deletions

File tree

‎pgcli/packages/parseutils/meta.py‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,13 @@ def fields(self):
158158
return [ColumnMetadata(self.func_name, self.return_type, [])]
159159

160160
# arg_modes being truthy doesn't guarantee arg_names/arg_types are
161-
# populated too (e.g. an unnamed variadic parameter), so fall back
162-
# to None placeholders the same way args() falls back for modes.
163-
names = self.arg_names or [None] * len(self.arg_modes)
164-
types = self.arg_types or [None] * len(self.arg_modes)
165-
return [
166-
ColumnMetadata(name, typ, []) for name, typ, mode in zip(names, types, self.arg_modes) if mode in ("o", "b", "t")
167-
] # OUT, INOUT, TABLE
161+
# populated too (e.g. an unnamed variadic parameter or a TABLE(...)
162+
# return without argument names).
163+
fields = [
164+
ColumnMetadata(name, typ, [])
165+
for name, typ, mode in zip(self.arg_names or [], self.arg_types or [], self.arg_modes)
166+
if mode in ("o", "b", "t") # OUT, INOUT, TABLE
167+
]
168+
# Without any usable output column, fall back to the function name,
169+
# as for functions declared without output parameters.
170+
return fields or [ColumnMetadata(self.func_name, self.return_type, [])]

‎tests/parseutils/test_function_metadata.py‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from pgcli.packages.parseutils.meta import FunctionMetadata
1+
from pgcli.packages.parseutils.meta import ColumnMetadata, FunctionMetadata
2+
from pgcli.pgcompleter import generate_alias
23

34

45
def test_function_metadata_eq():
@@ -16,14 +17,20 @@ def test_function_metadata_eq():
1617
def test_function_metadata_fields_with_variadic_and_no_arg_names():
1718
# Regression test: arg_modes being truthy doesn't guarantee arg_names is
1819
# populated (e.g. an unnamed variadic parameter). fields() used to crash
19-
# with "'NoneType' object is not iterable" instead of returning [].
20+
# with "'NoneType' object is not iterable".
2021
f = FunctionMetadata("s", "labels", None, ["text[]"], ["v"], "hstore", False, False, False, False, None)
21-
assert f.fields() == []
22+
assert f.fields() == [ColumnMetadata("labels", "hstore", [])]
2223

2324

2425
def test_function_metadata_fields_table_mode_with_no_arg_names():
26+
# Without argument names there is no output column name to offer, so the
27+
# function name is used and generate_alias() gets a real string.
2528
f = FunctionMetadata("s", "f", None, ["int4", "text"], ["t", "t"], "record", False, False, True, False, None)
2629
fields = f.fields()
27-
assert len(fields) == 2
28-
assert [field.datatype for field in fields] == ["int4", "text"]
29-
assert [field.name for field in fields] == [None, None]
30+
assert fields == [ColumnMetadata("f", "record", [])]
31+
assert all(generate_alias(field.name) for field in fields)
32+
33+
34+
def test_function_metadata_fields_table_mode_with_arg_names():
35+
f = FunctionMetadata("s", "f", ["a", "b"], ["int4", "text"], ["t", "t"], "record", False, False, True, False, None)
36+
assert f.fields() == [ColumnMetadata("a", "int4", []), ColumnMetadata("b", "text", [])]

0 commit comments

Comments
 (0)