From 0ab34a4041cfe434160369ab94a29bc8ecbc16fe Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Thu, 20 Feb 2025 09:39:30 +0000 Subject: [PATCH 1/5] chore: add uuid support Signed-off-by: Sri Harsha CH --- google/cloud/spanner_v1/_helpers.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/google/cloud/spanner_v1/_helpers.py b/google/cloud/spanner_v1/_helpers.py index aa58c59199..8a200fe812 100644 --- a/google/cloud/spanner_v1/_helpers.py +++ b/google/cloud/spanner_v1/_helpers.py @@ -21,6 +21,7 @@ import base64 import threading import logging +import uuid from google.protobuf.struct_pb2 import ListValue from google.protobuf.struct_pb2 import Value @@ -298,6 +299,8 @@ def _make_value_pb(value): return Value(string_value=base64.b64encode(value)) if isinstance(value, Interval): return Value(string_value=str(value)) + if isinstance(value, uuid.UUID): + return Value(string_value=str(value)) raise ValueError("Unknown type: %s" % (value,)) @@ -399,6 +402,8 @@ def _get_type_decoder(field_type, field_name, column_info=None): return _parse_numeric elif type_code == TypeCode.JSON: return _parse_json + elif type_code == TypeCode.UUID: + return _parse_uuid elif type_code == TypeCode.PROTO: return lambda value_pb: _parse_proto(value_pb, column_info, field_name) elif type_code == TypeCode.ENUM: @@ -481,6 +486,10 @@ def _parse_json(value_pb): return JsonObject.from_str(value_pb.string_value) +def _parse_uuid(value_pb): + return uuid.UUID(value_pb.string_value) + + def _parse_proto(value_pb, column_info, field_name): bytes_value = base64.b64decode(value_pb.string_value) if column_info is not None and column_info.get(field_name) is not None: From ff1f374120dd23d7342d05d37201291481d234c4 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Thu, 20 Feb 2025 09:46:01 +0000 Subject: [PATCH 2/5] chore: expose type Signed-off-by: Sri Harsha CH --- google/cloud/spanner_v1/param_types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/google/cloud/spanner_v1/param_types.py b/google/cloud/spanner_v1/param_types.py index 72127c0e0b..a5da41601a 100644 --- a/google/cloud/spanner_v1/param_types.py +++ b/google/cloud/spanner_v1/param_types.py @@ -33,6 +33,7 @@ TIMESTAMP = Type(code=TypeCode.TIMESTAMP) NUMERIC = Type(code=TypeCode.NUMERIC) JSON = Type(code=TypeCode.JSON) +UUID = Type(code=TypeCode.UUID) PG_NUMERIC = Type(code=TypeCode.NUMERIC, type_annotation=TypeAnnotationCode.PG_NUMERIC) PG_JSONB = Type(code=TypeCode.JSON, type_annotation=TypeAnnotationCode.PG_JSONB) PG_OID = Type(code=TypeCode.INT64, type_annotation=TypeAnnotationCode.PG_OID) From c4bde68862076478d9c93a6b3d50639709e10891 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Tue, 25 Feb 2025 11:30:27 +0000 Subject: [PATCH 3/5] chore: add unit and system tests for UUID Signed-off-by: Sri Harsha CH --- google/cloud/spanner_v1/streamed.py | 1 + tests/system/test_session_api.py | 12 ++++++++++++ tests/unit/test__helpers.py | 14 ++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/google/cloud/spanner_v1/streamed.py b/google/cloud/spanner_v1/streamed.py index c41e65d39f..e0002141f9 100644 --- a/google/cloud/spanner_v1/streamed.py +++ b/google/cloud/spanner_v1/streamed.py @@ -394,6 +394,7 @@ def _merge_struct(lhs, rhs, type_): TypeCode.PROTO: _merge_string, TypeCode.INTERVAL: _merge_string, TypeCode.ENUM: _merge_string, + TypeCode.UUID: _merge_string, } diff --git a/tests/system/test_session_api.py b/tests/system/test_session_api.py index 2b0caba4e1..8beca7a15f 100644 --- a/tests/system/test_session_api.py +++ b/tests/system/test_session_api.py @@ -20,6 +20,8 @@ import struct import threading import time +import uuid + import pytest import grpc @@ -3056,6 +3058,16 @@ def test_execute_sql_returning_transfinite_floats(sessions_database, not_postgre assert math.isnan(float_array[2]) +def test_execute_sql_w_uuid_bindings(sessions_database, database_dialect): + _bind_test_helper( + sessions_database, + database_dialect, + spanner_v1.param_types.UUID, + uuid.uuid4(), + [uuid.uuid4(), uuid.uuid4()], + ) + + def test_partition_query(sessions_database, not_emulator, not_experimental_host): row_count = 40 sql = f"SELECT * FROM {_sample_data.TABLE}" diff --git a/tests/unit/test__helpers.py b/tests/unit/test__helpers.py index 40db14607c..3c5a96e0f7 100644 --- a/tests/unit/test__helpers.py +++ b/tests/unit/test__helpers.py @@ -14,6 +14,8 @@ import unittest +import uuid + import mock from opentelemetry.sdk.resources import Resource @@ -786,6 +788,18 @@ def test_w_proto_enum(self): self._callFUT(value_pb, field_type, field_name, column_info), VALUE ) + def test_w_uuid(self): + from google.protobuf.struct_pb2 import Value + from google.cloud.spanner_v1 import Type + from google.cloud.spanner_v1 import TypeCode + + VALUE = uuid.uuid4() + field_type = Type(code=TypeCode.UUID) + field_name = "uuid_column" + value_pb = Value(string_value=str(VALUE)) + + self.assertEqual(self._callFUT(value_pb, field_type, field_name), VALUE) + class Test_parse_list_value_pbs(unittest.TestCase): def _callFUT(self, *args, **kw): From 2f5627f518dc89c0d21877e5493afef7a4381f25 Mon Sep 17 00:00:00 2001 From: Sri Harsha CH Date: Tue, 25 Feb 2025 11:33:43 +0000 Subject: [PATCH 4/5] chore: lint fix Signed-off-by: Sri Harsha CH --- tests/system/test_session_api.py | 1 - tests/unit/test__helpers.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/system/test_session_api.py b/tests/system/test_session_api.py index 8beca7a15f..e8877ebd6c 100644 --- a/tests/system/test_session_api.py +++ b/tests/system/test_session_api.py @@ -21,7 +21,6 @@ import threading import time import uuid - import pytest import grpc diff --git a/tests/unit/test__helpers.py b/tests/unit/test__helpers.py index 3c5a96e0f7..8140ecb1be 100644 --- a/tests/unit/test__helpers.py +++ b/tests/unit/test__helpers.py @@ -15,7 +15,6 @@ import unittest import uuid - import mock from opentelemetry.sdk.resources import Resource From 00ec0bfe64d2d3cd74d3b8006e548ba3ca23c6e5 Mon Sep 17 00:00:00 2001 From: Subham Sinha Date: Tue, 6 Jan 2026 16:04:29 +0530 Subject: [PATCH 5/5] feat(tests): skip uuid binding test for postgresql --- tests/system/test_session_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/system/test_session_api.py b/tests/system/test_session_api.py index e8877ebd6c..96f5cd76dc 100644 --- a/tests/system/test_session_api.py +++ b/tests/system/test_session_api.py @@ -3058,6 +3058,8 @@ def test_execute_sql_returning_transfinite_floats(sessions_database, not_postgre def test_execute_sql_w_uuid_bindings(sessions_database, database_dialect): + if database_dialect == DatabaseDialect.POSTGRESQL: + pytest.skip("UUID parameter type is not yet supported in PostgreSQL dialect.") _bind_test_helper( sessions_database, database_dialect,