From a801a6727c31e61e4e6ae0bcb8560ea6ac715a6a Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Fri, 2 Aug 2024 16:48:12 +0300 Subject: [PATCH 1/7] typed parameters --- examples/query-service/basic_example.py | 34 ++++++++++++++++++++----- ydb/_grpc/grpcwrapper/ydb_query.py | 4 ++- ydb/convert.py | 33 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/examples/query-service/basic_example.py b/examples/query-service/basic_example.py index b355e10c..ad164bef 100644 --- a/examples/query-service/basic_example.py +++ b/examples/query-service/basic_example.py @@ -16,15 +16,15 @@ def main(): pool = ydb.QuerySessionPool(driver) - print("=" * 50) - print("DELETE TABLE IF EXISTS") - pool.execute_with_retries("DROP TABLE IF EXISTS example") + # print("=" * 50) + # print("DELETE TABLE IF EXISTS") + # pool.execute_with_retries("drop table if exists example") - print("=" * 50) - print("CREATE TABLE") - pool.execute_with_retries("CREATE TABLE example(key UInt64, value String, PRIMARY KEY (key))") + # print("=" * 50) + # print("CREATE TABLE") + # pool.execute_with_retries("CREATE TABLE example(key UInt64, value String, PRIMARY KEY (key))") - pool.execute_with_retries("INSERT INTO example (key, value) VALUES (1, 'onepieceisreal')") + # pool.execute_with_retries("INSERT INTO example (key, value) VALUES (1, 'onepieceisreal')") def callee(session): print("=" * 50) @@ -82,6 +82,26 @@ def callee(session): pool.retry_operation_sync(callee) + query_print = """ + select $a + """ + + def callee(session: ydb.QuerySessionSync): + print("=" * 50) + print("Check typed parameters") + + values = [1, 1.0, True, "text"] + + for value in values: + print(f"value: {value}") + with session.transaction().execute( + query=query_print, parameters={'$a': value}, commit_tx=True + ) as results: + for result_set in results: + print(f"rows: {str(result_set.rows)}") + + pool.retry_operation_sync(callee) + if __name__ == "__main__": main() diff --git a/ydb/_grpc/grpcwrapper/ydb_query.py b/ydb/_grpc/grpcwrapper/ydb_query.py index befb02c7..913b8480 100644 --- a/ydb/_grpc/grpcwrapper/ydb_query.py +++ b/ydb/_grpc/grpcwrapper/ydb_query.py @@ -18,6 +18,8 @@ ServerStatus, ) +from ... import convert + @dataclass class CreateSessionResponse(IFromProto): @@ -176,5 +178,5 @@ def to_proto(self) -> ydb_query_pb2.ExecuteQueryRequest: exec_mode=self.exec_mode, stats_mode=self.stats_mode, concurrent_result_sets=self.concurrent_result_sets, - parameters=self.parameters, + parameters=convert.query_parameters_to_pb(self.parameters), ) diff --git a/ydb/convert.py b/ydb/convert.py index 6c4164bc..d898e1ef 100644 --- a/ydb/convert.py +++ b/ydb/convert.py @@ -281,6 +281,39 @@ def parameters_to_pb(parameters_types, parameters_values): return param_values_pb +def query_parameters_to_pb(parameters): + if parameters is None or not parameters: + return {} + + parameters_types = {} + parameters_values = {} + for name, value in parameters.items(): + if isinstance(value, tuple): + parameters_values[name] = value[0] + parameters_types[name] = value[1] + else: + parameters_values[name] = value + parameters_types[name] = _primitive_type_from_python_native(value) + + return parameters_to_pb(parameters_types, parameters_values) + + +_from_python_type_map = { + int: types.PrimitiveType.Int64, + float: types.PrimitiveType.Float, + dict: types.PrimitiveType.Json, + bool: types.PrimitiveType.Bool, + str: types.PrimitiveType.Utf8, +} + + +def _primitive_type_from_python_native(value): + t = type(value) + if t not in _from_python_type_map: + return types.PrimitiveType.Int64 + return _from_python_type_map[t] + + def _unwrap_optionality(column): c_type = column.type current_type = c_type.WhichOneof("type") From 643bc020f269a5e7cba6407f191eed8b762fffe0 Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Fri, 2 Aug 2024 16:48:12 +0300 Subject: [PATCH 2/7] parameters tests --- examples/query-service/basic_example.py | 4 +- tests/query/test_query_parameters.py | 56 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 tests/query/test_query_parameters.py diff --git a/examples/query-service/basic_example.py b/examples/query-service/basic_example.py index ad164bef..02b3a626 100644 --- a/examples/query-service/basic_example.py +++ b/examples/query-service/basic_example.py @@ -94,9 +94,7 @@ def callee(session: ydb.QuerySessionSync): for value in values: print(f"value: {value}") - with session.transaction().execute( - query=query_print, parameters={'$a': value}, commit_tx=True - ) as results: + with session.transaction().execute(query=query_print, parameters={'$a': value}, commit_tx=True) as results: for result_set in results: print(f"rows: {str(result_set.rows)}") diff --git a/tests/query/test_query_parameters.py b/tests/query/test_query_parameters.py new file mode 100644 index 00000000..0de0976d --- /dev/null +++ b/tests/query/test_query_parameters.py @@ -0,0 +1,56 @@ +import pytest +import ydb + + +query = """SELECT $a AS value""" + + +def test_select_implicit_int(pool: ydb.QuerySessionPool): + expected_value = 111 + res = pool.execute_with_retries(query, parameters={'$a': expected_value}) + actual_value = res[0].rows[0]['value'] + assert expected_value == actual_value + + +def test_select_implicit_float(pool: ydb.QuerySessionPool): + expected_value = 11.1 + res = pool.execute_with_retries(query, parameters={'$a': expected_value}) + actual_value = res[0].rows[0]['value'] + assert expected_value == pytest.approx(actual_value) + + +def test_select_implicit_bool(pool: ydb.QuerySessionPool): + expected_value = False + res = pool.execute_with_retries(query, parameters={'$a': expected_value}) + actual_value = res[0].rows[0]['value'] + assert expected_value == actual_value + + +def test_select_implicit_str(pool: ydb.QuerySessionPool): + expected_value = "text" + res = pool.execute_with_retries(query, parameters={'$a': expected_value}) + actual_value = res[0].rows[0]['value'] + assert expected_value == actual_value + + +def test_select_explicit_primitive(pool: ydb.QuerySessionPool): + expected_value = 111 + res = pool.execute_with_retries(query, parameters={'$a': (expected_value, ydb.PrimitiveType.Int64)}) + actual_value = res[0].rows[0]['value'] + assert expected_value == actual_value + + +def test_select_explicit_list(pool: ydb.QuerySessionPool): + expected_value = [1, 2, 3] + type_ = ydb.ListType(ydb.PrimitiveType.Int64) + res = pool.execute_with_retries(query, parameters={'$a': (expected_value, type_)}) + actual_value = res[0].rows[0]['value'] + assert expected_value == actual_value + + +def test_select_explicit_dict(pool: ydb.QuerySessionPool): + expected_value = {'key': 'value'} + type_ = ydb.DictType(ydb.PrimitiveType.Utf8, ydb.PrimitiveType.Utf8) + res = pool.execute_with_retries(query, parameters={'$a': (expected_value, type_)}) + actual_value = res[0].rows[0]['value'] + assert expected_value == actual_value From ea6fbb9a6fdd5fe22ee8d224b3c9884b53702494 Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Fri, 2 Aug 2024 16:48:12 +0300 Subject: [PATCH 3/7] Ability to use implicit lists and dicts --- examples/query-service/basic_example.py | 16 +++---- tests/query/test_query_parameters.py | 58 ++++++++++++++++++------- ydb/convert.py | 24 ++++++++-- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/examples/query-service/basic_example.py b/examples/query-service/basic_example.py index 02b3a626..1c4eb28e 100644 --- a/examples/query-service/basic_example.py +++ b/examples/query-service/basic_example.py @@ -16,15 +16,15 @@ def main(): pool = ydb.QuerySessionPool(driver) - # print("=" * 50) - # print("DELETE TABLE IF EXISTS") - # pool.execute_with_retries("drop table if exists example") + print("=" * 50) + print("DELETE TABLE IF EXISTS") + pool.execute_with_retries("drop table if exists example") - # print("=" * 50) - # print("CREATE TABLE") - # pool.execute_with_retries("CREATE TABLE example(key UInt64, value String, PRIMARY KEY (key))") + print("=" * 50) + print("CREATE TABLE") + pool.execute_with_retries("CREATE TABLE example(key UInt64, value String, PRIMARY KEY (key))") - # pool.execute_with_retries("INSERT INTO example (key, value) VALUES (1, 'onepieceisreal')") + pool.execute_with_retries("INSERT INTO example (key, value) VALUES (1, 'onepieceisreal')") def callee(session): print("=" * 50) @@ -94,7 +94,7 @@ def callee(session: ydb.QuerySessionSync): for value in values: print(f"value: {value}") - with session.transaction().execute(query=query_print, parameters={'$a': value}, commit_tx=True) as results: + with session.transaction().execute(query=query_print, parameters={"$a": value}, commit_tx=True) as results: for result_set in results: print(f"rows: {str(result_set.rows)}") diff --git a/tests/query/test_query_parameters.py b/tests/query/test_query_parameters.py index 0de0976d..88a6a8f3 100644 --- a/tests/query/test_query_parameters.py +++ b/tests/query/test_query_parameters.py @@ -7,50 +7,78 @@ def test_select_implicit_int(pool: ydb.QuerySessionPool): expected_value = 111 - res = pool.execute_with_retries(query, parameters={'$a': expected_value}) - actual_value = res[0].rows[0]['value'] + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] assert expected_value == actual_value def test_select_implicit_float(pool: ydb.QuerySessionPool): expected_value = 11.1 - res = pool.execute_with_retries(query, parameters={'$a': expected_value}) - actual_value = res[0].rows[0]['value'] + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] assert expected_value == pytest.approx(actual_value) def test_select_implicit_bool(pool: ydb.QuerySessionPool): expected_value = False - res = pool.execute_with_retries(query, parameters={'$a': expected_value}) - actual_value = res[0].rows[0]['value'] + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] assert expected_value == actual_value def test_select_implicit_str(pool: ydb.QuerySessionPool): expected_value = "text" - res = pool.execute_with_retries(query, parameters={'$a': expected_value}) - actual_value = res[0].rows[0]['value'] + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + + +def test_select_implicit_list(pool: ydb.QuerySessionPool): + expected_value = [1, 2, 3] + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + + +def test_select_implicit_dict(pool: ydb.QuerySessionPool): + expected_value = {"a": 1, "b": 2} + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + + +def test_select_implicit_list_nested(pool: ydb.QuerySessionPool): + expected_value = [{"a": 1}, {"b": 2}] + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + + +def test_select_implicit_dict_nested(pool: ydb.QuerySessionPool): + expected_value = {"a": [1, 2, 3], "b": [4, 5]} + res = pool.execute_with_retries(query, parameters={"$a": expected_value}) + actual_value = res[0].rows[0]["value"] assert expected_value == actual_value def test_select_explicit_primitive(pool: ydb.QuerySessionPool): expected_value = 111 - res = pool.execute_with_retries(query, parameters={'$a': (expected_value, ydb.PrimitiveType.Int64)}) - actual_value = res[0].rows[0]['value'] + res = pool.execute_with_retries(query, parameters={"$a": (expected_value, ydb.PrimitiveType.Int64)}) + actual_value = res[0].rows[0]["value"] assert expected_value == actual_value def test_select_explicit_list(pool: ydb.QuerySessionPool): expected_value = [1, 2, 3] type_ = ydb.ListType(ydb.PrimitiveType.Int64) - res = pool.execute_with_retries(query, parameters={'$a': (expected_value, type_)}) - actual_value = res[0].rows[0]['value'] + res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)}) + actual_value = res[0].rows[0]["value"] assert expected_value == actual_value def test_select_explicit_dict(pool: ydb.QuerySessionPool): - expected_value = {'key': 'value'} + expected_value = {"key": "value"} type_ = ydb.DictType(ydb.PrimitiveType.Utf8, ydb.PrimitiveType.Utf8) - res = pool.execute_with_retries(query, parameters={'$a': (expected_value, type_)}) - actual_value = res[0].rows[0]['value'] + res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)}) + actual_value = res[0].rows[0]["value"] assert expected_value == actual_value diff --git a/ydb/convert.py b/ydb/convert.py index d898e1ef..0eaed512 100644 --- a/ydb/convert.py +++ b/ydb/convert.py @@ -301,7 +301,6 @@ def query_parameters_to_pb(parameters): _from_python_type_map = { int: types.PrimitiveType.Int64, float: types.PrimitiveType.Float, - dict: types.PrimitiveType.Json, bool: types.PrimitiveType.Bool, str: types.PrimitiveType.Utf8, } @@ -309,9 +308,26 @@ def query_parameters_to_pb(parameters): def _primitive_type_from_python_native(value): t = type(value) - if t not in _from_python_type_map: - return types.PrimitiveType.Int64 - return _from_python_type_map[t] + default_type = types.PrimitiveType.Int64 + + if t in _from_python_type_map: + return _from_python_type_map[t] + + if t == list: + if len(value) == 0: + return types.ListType(default_type) + entry_type = _primitive_type_from_python_native(value[0]) + return types.ListType(entry_type) + + if t == dict: + if len(value) == 0: + return types.DictType(default_type, default_type) + entry = list(value.items())[0] + key_type = _primitive_type_from_python_native(entry[0]) + value_type = _primitive_type_from_python_native(entry[1]) + return types.DictType(key_type, value_type) + + return default_type def _unwrap_optionality(column): From c82caf00bda2d4c370a09ac8ea06e80d93179d0a Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Fri, 2 Aug 2024 16:48:12 +0300 Subject: [PATCH 4/7] TypedValue public interface --- tests/query/test_query_parameters.py | 62 ++++++++++++++++++++++++++++ ydb/convert.py | 30 ++++++++++---- ydb/types.py | 6 +++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/tests/query/test_query_parameters.py b/tests/query/test_query_parameters.py index 88a6a8f3..baf3de04 100644 --- a/tests/query/test_query_parameters.py +++ b/tests/query/test_query_parameters.py @@ -61,6 +61,27 @@ def test_select_implicit_dict_nested(pool: ydb.QuerySessionPool): assert expected_value == actual_value +def test_select_implicit_custom_type_raises(pool: ydb.QuerySessionPool): + class CustomClass: + pass + + expected_value = CustomClass() + with pytest.raises(ValueError): + pool.execute_with_retries(query, parameters={"$a": expected_value}) + + +def test_select_implicit_empty_list_raises(pool: ydb.QuerySessionPool): + expected_value = [] + with pytest.raises(ValueError): + pool.execute_with_retries(query, parameters={"$a": expected_value}) + + +def test_select_implicit_empty_dict_raises(pool: ydb.QuerySessionPool): + expected_value = {} + with pytest.raises(ValueError): + pool.execute_with_retries(query, parameters={"$a": expected_value}) + + def test_select_explicit_primitive(pool: ydb.QuerySessionPool): expected_value = 111 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): res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)}) actual_value = res[0].rows[0]["value"] assert expected_value == actual_value + + +def test_select_explicit_empty_list_not_raises(pool: ydb.QuerySessionPool): + expected_value = [] + type_ = ydb.ListType(ydb.PrimitiveType.Int64) + res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + + +def test_select_explicit_empty_dict_not_raises(pool: ydb.QuerySessionPool): + expected_value = {} + type_ = ydb.DictType(ydb.PrimitiveType.Utf8, ydb.PrimitiveType.Utf8) + res = pool.execute_with_retries(query, parameters={"$a": (expected_value, type_)}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + + +def test_select_typedvalue_full_primitive(pool: ydb.QuerySessionPool): + expected_value = 111 + typed_value = ydb.TypedValue(expected_value, ydb.PrimitiveType.Int64) + res = pool.execute_with_retries(query, parameters={"$a": typed_value}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + + +def test_select_typedvalue_implicit_primitive(pool: ydb.QuerySessionPool): + expected_value = 111 + typed_value = ydb.TypedValue(expected_value) + res = pool.execute_with_retries(query, parameters={"$a": typed_value}) + actual_value = res[0].rows[0]["value"] + assert expected_value == actual_value + +def test_select_typevalue_custom_type_raises(pool: ydb.QuerySessionPool): + class CustomClass: + pass + + expected_value = CustomClass() + typed_value = ydb.TypedValue(expected_value) + with pytest.raises(ValueError): + pool.execute_with_retries(query, parameters={"$a": typed_value}) diff --git a/ydb/convert.py b/ydb/convert.py index 0eaed512..986918c9 100644 --- a/ydb/convert.py +++ b/ydb/convert.py @@ -288,12 +288,16 @@ def query_parameters_to_pb(parameters): parameters_types = {} parameters_values = {} for name, value in parameters.items(): - if isinstance(value, tuple): - parameters_values[name] = value[0] - parameters_types[name] = value[1] + if isinstance(value, types.TypedValue): + if value.value_type is None: + value.value_type = _primitive_type_from_python_native(value.value) + elif isinstance(value, tuple): + value = types.TypedValue(*value) else: - parameters_values[name] = value - parameters_types[name] = _primitive_type_from_python_native(value) + value = types.TypedValue(value, _primitive_type_from_python_native(value)) + + parameters_values[name] = value.value + parameters_types[name] = value.value_type return parameters_to_pb(parameters_types, parameters_values) @@ -308,26 +312,34 @@ def query_parameters_to_pb(parameters): def _primitive_type_from_python_native(value): t = type(value) - default_type = types.PrimitiveType.Int64 if t in _from_python_type_map: return _from_python_type_map[t] if t == list: if len(value) == 0: - return types.ListType(default_type) + raise ValueError( + "Could not map empty list to any type, please specify " + "it manually by tuple(value, type) or ydb.TypedValue" + ) entry_type = _primitive_type_from_python_native(value[0]) return types.ListType(entry_type) if t == dict: if len(value) == 0: - return types.DictType(default_type, default_type) + raise ValueError( + "Could not map empty dict to any type, please specify " + "it manually by tuple(value, type) or ydb.TypedValue" + ) entry = list(value.items())[0] key_type = _primitive_type_from_python_native(entry[0]) value_type = _primitive_type_from_python_native(entry[1]) return types.DictType(key_type, value_type) - return default_type + raise ValueError( + "Could not map value to any type, please specify " + "it manually by tuple(value, type) or ydb.TypedValue" + ) def _unwrap_optionality(column): diff --git a/ydb/types.py b/ydb/types.py index 2a2a7e07..ef197ac0 100644 --- a/ydb/types.py +++ b/ydb/types.py @@ -2,6 +2,7 @@ from __future__ import annotations import abc +from dataclasses import dataclass import enum import json from . import _utilities, _apis @@ -441,3 +442,8 @@ def proto(self): def __str__(self): return "BulkUpsertColumns<%s>" % ",".join(self.__columns_repr) + +@dataclass +class TypedValue: + value: typing.Any + value_type: typing.Optional[typing.Union[PrimitiveType, AbstractTypeBuilder]] = None From d091436733a0e45c5167e2f7556277cb08ecd9cd Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Fri, 2 Aug 2024 16:48:12 +0300 Subject: [PATCH 5/7] style fixes --- tests/query/test_query_parameters.py | 1 + ydb/convert.py | 3 +-- ydb/types.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/query/test_query_parameters.py b/tests/query/test_query_parameters.py index baf3de04..ff033311 100644 --- a/tests/query/test_query_parameters.py +++ b/tests/query/test_query_parameters.py @@ -136,6 +136,7 @@ def test_select_typedvalue_implicit_primitive(pool: ydb.QuerySessionPool): actual_value = res[0].rows[0]["value"] assert expected_value == actual_value + def test_select_typevalue_custom_type_raises(pool: ydb.QuerySessionPool): class CustomClass: pass diff --git a/ydb/convert.py b/ydb/convert.py index 986918c9..2763e675 100644 --- a/ydb/convert.py +++ b/ydb/convert.py @@ -337,8 +337,7 @@ def _primitive_type_from_python_native(value): return types.DictType(key_type, value_type) raise ValueError( - "Could not map value to any type, please specify " - "it manually by tuple(value, type) or ydb.TypedValue" + "Could not map value to any type, please specify it manually by tuple(value, type) or ydb.TypedValue" ) diff --git a/ydb/types.py b/ydb/types.py index ef197ac0..f8a56e4d 100644 --- a/ydb/types.py +++ b/ydb/types.py @@ -443,6 +443,7 @@ def proto(self): def __str__(self): return "BulkUpsertColumns<%s>" % ",".join(self.__columns_repr) + @dataclass class TypedValue: value: typing.Any From 019594d8f100efab81a23d067a32d58c63baea48 Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Fri, 2 Aug 2024 16:48:12 +0300 Subject: [PATCH 6/7] extend query service example with parameters --- examples/query-service/basic_example.py | 53 +++++++++++++++++++++---- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/examples/query-service/basic_example.py b/examples/query-service/basic_example.py index 1c4eb28e..cfbb3042 100644 --- a/examples/query-service/basic_example.py +++ b/examples/query-service/basic_example.py @@ -18,7 +18,7 @@ def main(): print("=" * 50) print("DELETE TABLE IF EXISTS") - pool.execute_with_retries("drop table if exists example") + pool.execute_with_retries("DROP TABLE IF EXISTS example") print("=" * 50) print("CREATE TABLE") @@ -82,22 +82,59 @@ def callee(session): pool.retry_operation_sync(callee) - query_print = """ - select $a - """ - def callee(session: ydb.QuerySessionSync): + query_print = """select $a""" + print("=" * 50) - print("Check typed parameters") + print("Check implicit typed parameters") - values = [1, 1.0, True, "text"] + values = [ + 1, + 1.0, + True, + "text", + {"4": 8, "15": 16, "23": 42}, + [{"name": "Michael"}, {"surname": "Scott"}], + ] for value in values: print(f"value: {value}") - with session.transaction().execute(query=query_print, parameters={"$a": value}, commit_tx=True) as results: + with session.transaction().execute( + query=query_print, + parameters={"$a": value}, + commit_tx=True, + ) as results: for result_set in results: print(f"rows: {str(result_set.rows)}") + print("=" * 50) + print("Check typed parameters as tuple pair") + + typed_value = ([1, 2, 3], ydb.ListType(ydb.PrimitiveType.Int64)) + print(f"value: {typed_value}") + + with session.transaction().execute( + query=query_print, + parameters={"$a": typed_value}, + commit_tx=True, + ) as results: + for result_set in results: + print(f"rows: {str(result_set.rows)}") + + print("=" * 50) + print("Check typed parameters as ydb.TypedValue") + + typed_value = ydb.TypedValue(111, ydb.PrimitiveType.Int64) + print(f"value: {typed_value}") + + with session.transaction().execute( + query=query_print, + parameters={"$a": typed_value}, + commit_tx=True, + ) as results: + for result_set in results: + print(f"rows: {str(result_set.rows)}") + pool.retry_operation_sync(callee) From 90390c8072866a054e7b313a35495a6a525b496b Mon Sep 17 00:00:00 2001 From: Oleg Ovcharuk Date: Fri, 2 Aug 2024 16:48:12 +0300 Subject: [PATCH 7/7] review fix --- CHANGELOG.md | 3 +++ docker-compose-tls.yml | 1 + docker-compose.yml | 1 + ydb/convert.py | 12 ++++++------ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70bb5bc3..6306aa90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ + +* Query service client support +* Add dunder version to ydb package * OAuth 2.0 token exchange. Allow multiple resource parameters in according to https://www.rfc-editor.org/rfc/rfc8693 ## 3.14.0 ## diff --git a/docker-compose-tls.yml b/docker-compose-tls.yml index 19693705..f0a4b328 100644 --- a/docker-compose-tls.yml +++ b/docker-compose-tls.yml @@ -11,3 +11,4 @@ services: - ./ydb_certs:/ydb_certs environment: - YDB_USE_IN_MEMORY_PDISKS=true + - YDB_ENABLE_COLUMN_TABLES=true diff --git a/docker-compose.yml b/docker-compose.yml index cb37a377..1a466fab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,3 +8,4 @@ services: hostname: localhost environment: - YDB_USE_IN_MEMORY_PDISKS=true + - YDB_ENABLE_COLUMN_TABLES=true diff --git a/ydb/convert.py b/ydb/convert.py index 2763e675..63a5dbe4 100644 --- a/ydb/convert.py +++ b/ydb/convert.py @@ -290,11 +290,11 @@ def query_parameters_to_pb(parameters): for name, value in parameters.items(): if isinstance(value, types.TypedValue): if value.value_type is None: - value.value_type = _primitive_type_from_python_native(value.value) + value.value_type = _type_from_python_native(value.value) elif isinstance(value, tuple): value = types.TypedValue(*value) else: - value = types.TypedValue(value, _primitive_type_from_python_native(value)) + value = types.TypedValue(value, _type_from_python_native(value)) parameters_values[name] = value.value parameters_types[name] = value.value_type @@ -310,7 +310,7 @@ def query_parameters_to_pb(parameters): } -def _primitive_type_from_python_native(value): +def _type_from_python_native(value): t = type(value) if t in _from_python_type_map: @@ -322,7 +322,7 @@ def _primitive_type_from_python_native(value): "Could not map empty list to any type, please specify " "it manually by tuple(value, type) or ydb.TypedValue" ) - entry_type = _primitive_type_from_python_native(value[0]) + entry_type = _type_from_python_native(value[0]) return types.ListType(entry_type) if t == dict: @@ -332,8 +332,8 @@ def _primitive_type_from_python_native(value): "it manually by tuple(value, type) or ydb.TypedValue" ) entry = list(value.items())[0] - key_type = _primitive_type_from_python_native(entry[0]) - value_type = _primitive_type_from_python_native(entry[1]) + key_type = _type_from_python_native(entry[0]) + value_type = _type_from_python_native(entry[1]) return types.DictType(key_type, value_type) raise ValueError(