Skip to content

Commit 57244db

Browse files
swasikfruch
authored andcommitted
Fix vector type formatting error in cqlsh
CqlType.parse() treated vector<float, 5> as having two subtypes [float, 5]. When formatting a 5-element vector, get_n_sub_types(5) failed because len(sub_types) == 2 matched neither the tuple case (== num) nor the list/set case (== 1), raising: 'Unexpected number of subtypes 5 - [float, 5]' Fix: when parsing a vector<type, dimension> type string, discard the dimension and keep only the element type as a single subtype. This lets get_n_sub_types() replicate the element type for each value in the vector, matching the existing list/set code path. Added test_vector_formatting.py with 8 tests covering CqlType parsing and end-to-end value formatting for various vector types. Fixes: VECTOR-563
1 parent 71359f1 commit 57244db

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

pylib/cqlshlib/formatting.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ def parse(self, typestring, ksmeta):
168168
continue
169169

170170
name = m.group(1) # a composite type, parse sub types
171-
return name, self.parse_sub_types(m.group(2), ksmeta), self._get_formatter(name)
171+
sub_types = self.parse_sub_types(m.group(2), ksmeta)
172+
if name == 'vector':
173+
# vector<type, dimension> - keep only the element type, discard dimension
174+
sub_types = sub_types[:1]
175+
return name, sub_types, self._get_formatter(name)
172176

173177
@staticmethod
174178
def _get_formatter(name):
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
"""
18+
Tests for vector type formatting in cqlsh.
19+
"""
20+
21+
import unittest
22+
from cqlshlib.formatting import CqlType, format_by_type
23+
from cqlshlib.displaying import NO_COLOR_MAP, get_str
24+
25+
26+
class TestCqlTypeVectorParsing(unittest.TestCase):
27+
"""Test that vector<type, N> CQL types are parsed correctly."""
28+
29+
def test_vector_float_5_has_single_subtype(self):
30+
"""vector<float, 5> should have a single sub_type of 'float', not two sub_types [float, 5]."""
31+
cqltype = CqlType('vector<float, 5>')
32+
self.assertEqual(cqltype.type_name, 'vector')
33+
self.assertEqual(len(cqltype.sub_types), 1)
34+
self.assertEqual(cqltype.sub_types[0].type_name, 'float')
35+
36+
def test_vector_int_3_has_single_subtype(self):
37+
"""vector<int, 3> should have a single sub_type of 'int'."""
38+
cqltype = CqlType('vector<int, 3>')
39+
self.assertEqual(cqltype.type_name, 'vector')
40+
self.assertEqual(len(cqltype.sub_types), 1)
41+
self.assertEqual(cqltype.sub_types[0].type_name, 'int')
42+
43+
def test_vector_get_n_sub_types(self):
44+
"""get_n_sub_types should work for vector types with any number of elements."""
45+
cqltype = CqlType('vector<float, 5>')
46+
sub_types = cqltype.get_n_sub_types(5)
47+
self.assertEqual(len(sub_types), 5)
48+
for st in sub_types:
49+
self.assertEqual(st.type_name, 'float')
50+
51+
def test_frozen_vector(self):
52+
"""frozen<vector<float, 3>> should also parse correctly."""
53+
cqltype = CqlType('frozen<vector<float, 3>>')
54+
self.assertEqual(cqltype.type_name, 'vector')
55+
self.assertEqual(len(cqltype.sub_types), 1)
56+
self.assertEqual(cqltype.sub_types[0].type_name, 'float')
57+
58+
59+
class TestVectorValueFormatting(unittest.TestCase):
60+
"""Test that vector values format correctly without raising exceptions."""
61+
62+
def test_format_vector_float_5(self):
63+
"""
64+
Reproducer for the VECTOR-563: formatting a vector<float, 5> value should not raise
65+
'Unexpected number of subtypes 5 - [float, 5]'.
66+
"""
67+
cqltype = CqlType('vector<float, 5>')
68+
val = [0.10999999940395355, 0.3499999940395355, 0.550000011920929,
69+
0.7699999809265137, 0.9200000166893005]
70+
# This should not raise an exception
71+
result = format_by_type(val, cqltype=cqltype, encoding='utf-8',
72+
colormap=NO_COLOR_MAP, addcolor=False)
73+
result_str = get_str(result)
74+
self.assertEqual(result_str, '[0.11, 0.35, 0.55, 0.77, 0.92]')
75+
76+
def test_format_vector_int_3(self):
77+
"""Formatting vector<int, 3> should work."""
78+
cqltype = CqlType('vector<int, 3>')
79+
val = [1, 2, 3]
80+
result = format_by_type(val, cqltype=cqltype, encoding='utf-8',
81+
colormap=NO_COLOR_MAP, addcolor=False)
82+
result_str = get_str(result)
83+
self.assertEqual(result_str, '[1, 2, 3]')
84+
85+
def test_format_empty_vector(self):
86+
"""Formatting an empty vector should work."""
87+
cqltype = CqlType('vector<float, 5>')
88+
val = []
89+
result = format_by_type(val, cqltype=cqltype, encoding='utf-8',
90+
colormap=NO_COLOR_MAP, addcolor=False)
91+
self.assertIsNotNone(result)
92+
self.assertEqual(get_str(result), '[]')
93+
94+
def test_format_vector_single_element(self):
95+
"""Formatting vector<float, 1> should work."""
96+
cqltype = CqlType('vector<float, 1>')
97+
val = [0.5]
98+
result = format_by_type(val, cqltype=cqltype, encoding='utf-8',
99+
colormap=NO_COLOR_MAP, addcolor=False)
100+
result_str = get_str(result)
101+
self.assertEqual(result_str, '[0.5]')
102+
103+
104+
if __name__ == '__main__':
105+
unittest.main()

0 commit comments

Comments
 (0)