Skip to content

Commit 93d2130

Browse files
feat(sqlalchemy-spanner): native UUID data type support in dialect
Adds native Spanner UUID type support to SQLAlchemy dialect: - Registers "UUID": types.UUID in _type_map for reflection - Registers types.UUID: "UUID" in _type_map_inv - Implements visit_UUID and visit_uuid in SpannerTypeCompiler
1 parent 5b2da81 commit 93d2130

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

packages/sqlalchemy-spanner/google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def process(value):
120120
"TIMESTAMP": types.TIMESTAMP,
121121
"ARRAY": types.ARRAY,
122122
"JSON": types.JSON,
123+
"UUID": types.UUID,
123124
}
124125

125126

@@ -138,6 +139,7 @@ def process(value):
138139
types.TIMESTAMP: "TIMESTAMP",
139140
types.Integer: "INT64",
140141
types.NullType: "INT64",
142+
types.UUID: "UUID",
141143
}
142144

143145
_compound_keywords = {
@@ -764,6 +766,9 @@ class SpannerTypeCompiler(GenericTypeCompiler):
764766
Maps SQLAlchemy types to Spanner data types.
765767
"""
766768

769+
def visit_UUID(self, type_, **kw):
770+
return "UUID"
771+
767772
def visit_INTEGER(self, type_, **kw):
768773
return "INT64"
769774

@@ -846,6 +851,7 @@ class SpannerDialect(DefaultDialect):
846851
supports_identity_columns = True
847852
supports_native_boolean = True
848853
supports_native_decimal = True
854+
supports_native_uuid = True
849855
supports_statement_cache = True
850856
# Spanner uses protos for enums. Creating a column like
851857
# Column("an_enum", Enum("A", "B", "C")) will result in a String
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from sqlalchemy import Column, MetaData, Table, types
16+
from sqlalchemy.schema import CreateTable
17+
from sqlalchemy.testing import eq_, fixtures
18+
from google.cloud.sqlalchemy_spanner.sqlalchemy_spanner import (
19+
SpannerDialect,
20+
_type_map,
21+
_type_map_inv,
22+
)
23+
24+
25+
class UuidTest(fixtures.TestBase):
26+
def test_uuid_type_mapping(self):
27+
"""Test UUID is registered in _type_map and _type_map_inv."""
28+
assert "UUID" in _type_map
29+
eq_(_type_map["UUID"], types.UUID)
30+
assert types.UUID in _type_map_inv
31+
eq_(_type_map_inv[types.UUID], "UUID")
32+
33+
def test_uuid_designate_type(self):
34+
"""Test reflecting UUID type string returns types.UUID."""
35+
dialect = SpannerDialect()
36+
assert dialect.supports_native_uuid is True
37+
col_type = dialect._designate_type("UUID")
38+
eq_(col_type, types.UUID)
39+
40+
def test_uuid_ddl_compilation_native_enabled(self):
41+
"""Test DDL compilation for UUID column emits UUID type when native."""
42+
dialect = SpannerDialect()
43+
metadata = MetaData()
44+
table = Table(
45+
"test_uuid_table",
46+
metadata,
47+
Column("user_id", types.Uuid, primary_key=True),
48+
)
49+
statement = str(CreateTable(table).compile(dialect=dialect)).strip()
50+
assert "user_id UUID NOT NULL" in statement
51+
52+
def test_uuid_ddl_compilation_native_disabled(self):
53+
"""Test DDL compilation falls back to CHAR(32) when native_uuid is False."""
54+
dialect = SpannerDialect()
55+
dialect.supports_native_uuid = False
56+
metadata = MetaData()
57+
table = Table(
58+
"test_uuid_table",
59+
metadata,
60+
Column("user_id", types.Uuid, primary_key=True),
61+
)
62+
statement = str(CreateTable(table).compile(dialect=dialect)).strip()
63+
assert "user_id CHAR(32) NOT NULL" in statement

0 commit comments

Comments
 (0)