Skip to content

Commit 5f972fa

Browse files
committed
TypedValue public interface
1 parent 99d43cc commit 5f972fa

File tree

3 files changed

+89
-9
lines changed

3 files changed

+89
-9
lines changed

tests/query/test_query_parameters.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,27 @@ def test_select_implicit_dict_nested(pool: ydb.QuerySessionPool):
6161
assert expected_value == actual_value
6262

6363

64+
def test_select_implicit_custom_type_raises(pool: ydb.QuerySessionPool):
65+
class CustomClass:
66+
pass
67+
68+
expected_value = CustomClass()
69+
with pytest.raises(ValueError):
70+
pool.execute_with_retries(query, parameters={"$a": expected_value})
71+
72+
73+
def test_select_implicit_empty_list_raises(pool: ydb.QuerySessionPool):
74+
expected_value = []
75+
with pytest.raises(ValueError):
76+
pool.execute_with_retries(query, parameters={"$a": expected_value})
77+
78+
79+
def test_select_implicit_empty_dict_raises(pool: ydb.QuerySessionPool):
80+
expected_value = {}
81+
with pytest.raises(ValueError):
82+
pool.execute_with_retries(query, parameters={"$a": expected_value})
83+
84+
6485
def test_select_explicit_primitive(pool: ydb.QuerySessionPool):
6586
expected_value = 111
6687
res = pool.execute_with_retries(query, parameters={"$a": (expected_value, ydb.PrimitiveType.Int64)})
@@ -82,3 +103,44 @@ def test_select_explicit_dict(pool: ydb.QuerySessionPool):
82103
res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)})
83104
actual_value = res[0].rows[0]["value"]
84105
assert expected_value == actual_value
106+
107+
108+
def test_select_explicit_empty_list_not_raises(pool: ydb.QuerySessionPool):
109+
expected_value = []
110+
type_ = ydb.ListType(ydb.PrimitiveType.Int64)
111+
res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)})
112+
actual_value = res[0].rows[0]["value"]
113+
assert expected_value == actual_value
114+
115+
116+
def test_select_explicit_empty_dict_not_raises(pool: ydb.QuerySessionPool):
117+
expected_value = {}
118+
type_ = ydb.DictType(ydb.PrimitiveType.Utf8, ydb.PrimitiveType.Utf8)
119+
res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)})
120+
actual_value = res[0].rows[0]["value"]
121+
assert expected_value == actual_value
122+
123+
124+
def test_select_typedvalue_full_primitive(pool: ydb.QuerySessionPool):
125+
expected_value = 111
126+
typed_value = ydb.TypedValue(expected_value, ydb.PrimitiveType.Int64)
127+
res = pool.execute_with_retries(query, parameters={"$a": typed_value})
128+
actual_value = res[0].rows[0]["value"]
129+
assert expected_value == actual_value
130+
131+
132+
def test_select_typedvalue_implicit_primitive(pool: ydb.QuerySessionPool):
133+
expected_value = 111
134+
typed_value = ydb.TypedValue(expected_value)
135+
res = pool.execute_with_retries(query, parameters={"$a": typed_value})
136+
actual_value = res[0].rows[0]["value"]
137+
assert expected_value == actual_value
138+
139+
def test_select_typevalue_custom_type_raises(pool: ydb.QuerySessionPool):
140+
class CustomClass:
141+
pass
142+
143+
expected_value = CustomClass()
144+
typed_value = ydb.TypedValue(expected_value)
145+
with pytest.raises(ValueError):
146+
pool.execute_with_retries(query, parameters={"$a": typed_value})

ydb/convert.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,16 @@ def query_parameters_to_pb(parameters):
288288
parameters_types = {}
289289
parameters_values = {}
290290
for name, value in parameters.items():
291-
if isinstance(value, tuple):
292-
parameters_values[name] = value[0]
293-
parameters_types[name] = value[1]
291+
if isinstance(value, types.TypedValue):
292+
if value.value_type is None:
293+
value.value_type = _primitive_type_from_python_native(value.value)
294+
elif isinstance(value, tuple):
295+
value = types.TypedValue(*value)
294296
else:
295-
parameters_values[name] = value
296-
parameters_types[name] = _primitive_type_from_python_native(value)
297+
value = types.TypedValue(value, _primitive_type_from_python_native(value))
298+
299+
parameters_values[name] = value.value
300+
parameters_types[name] = value.value_type
297301

298302
return parameters_to_pb(parameters_types, parameters_values)
299303

@@ -308,26 +312,34 @@ def query_parameters_to_pb(parameters):
308312

309313
def _primitive_type_from_python_native(value):
310314
t = type(value)
311-
default_type = types.PrimitiveType.Int64
312315

313316
if t in _from_python_type_map:
314317
return _from_python_type_map[t]
315318

316319
if t == list:
317320
if len(value) == 0:
318-
return types.ListType(default_type)
321+
raise ValueError(
322+
"Could not map empty list to any type, please specify "
323+
"it manually by tuple(value, type) or ydb.TypedValue"
324+
)
319325
entry_type = _primitive_type_from_python_native(value[0])
320326
return types.ListType(entry_type)
321327

322328
if t == dict:
323329
if len(value) == 0:
324-
return types.DictType(default_type, default_type)
330+
raise ValueError(
331+
"Could not map empty dict to any type, please specify "
332+
"it manually by tuple(value, type) or ydb.TypedValue"
333+
)
325334
entry = list(value.items())[0]
326335
key_type = _primitive_type_from_python_native(entry[0])
327336
value_type = _primitive_type_from_python_native(entry[1])
328337
return types.DictType(key_type, value_type)
329338

330-
return default_type
339+
raise ValueError(
340+
"Could not map value to any type, please specify "
341+
"it manually by tuple(value, type) or ydb.TypedValue"
342+
)
331343

332344

333345
def _unwrap_optionality(column):

ydb/types.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import annotations
33

44
import abc
5+
from dataclasses import dataclass
56
import enum
67
import json
78
from . import _utilities, _apis
@@ -441,3 +442,8 @@ def proto(self):
441442

442443
def __str__(self):
443444
return "BulkUpsertColumns<%s>" % ",".join(self.__columns_repr)
445+
446+
@dataclass
447+
class TypedValue:
448+
value: typing.Any
449+
value_type: typing.Optional[typing.Union[PrimitiveType, AbstractTypeBuilder]] = None

0 commit comments

Comments
 (0)