-
Notifications
You must be signed in to change notification settings - Fork 9
Support YDB native UUID type #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import asyncio | ||
import datetime | ||
import uuid | ||
from decimal import Decimal | ||
from typing import NamedTuple | ||
|
||
|
@@ -11,6 +12,7 @@ | |
from ydb._grpc.v4.protos import ydb_common_pb2 | ||
|
||
from ydb_sqlalchemy import IsolationLevel, dbapi | ||
|
||
from ydb_sqlalchemy import sqlalchemy as ydb_sa | ||
from ydb_sqlalchemy.sqlalchemy import types | ||
|
||
|
@@ -238,6 +240,13 @@ def define_tables(cls, metadata: sa.MetaData): | |
Column("date", sa.Date), | ||
# Column("interval", sa.Interval), | ||
) | ||
Table( | ||
"test_uuid_types", | ||
metadata, | ||
Column("id", Integer, primary_key=True), | ||
Column("uuid_native", sa.UUID), | ||
Column("uuid_str", sa.Uuid), | ||
) | ||
|
||
def test_primitive_types(self, connection): | ||
table = self.tables.test_primitive_types | ||
|
@@ -310,6 +319,22 @@ def test_datetime_types_timezone(self, connection): | |
today, | ||
) | ||
|
||
def test_uuid_types(self, connection): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (non-blocking): Consider to add test that |
||
table = self.tables.test_uuid_types | ||
uuid_value = uuid.uuid4() | ||
|
||
statement = sa.insert(table).values(id=1, uuid_native=uuid_value, uuid_str=uuid_value) | ||
connection.execute(statement) | ||
row = connection.execute(sa.select(table).where(table.c.id == 1)).fetchone() | ||
assert row == (1, uuid_value, uuid_value) | ||
|
||
uuid_value_str = str(uuid_value) | ||
|
||
statement = sa.insert(table).values(id=2, uuid_native=uuid_value_str, uuid_str=uuid_value) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Shouldn't here be also |
||
connection.execute(statement) | ||
row = connection.execute(sa.select(table).where(table.c.id == 2)).fetchone() | ||
assert row == (2, uuid_value, uuid_value) | ||
|
||
|
||
class TestWithClause(TablesTest): | ||
__backend__ = True | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,19 +11,29 @@ | |
BaseYqlIdentifierPreparer, | ||
BaseYqlTypeCompiler, | ||
) | ||
from .. import types | ||
from typing import Union | ||
|
||
|
||
class YqlTypeCompiler(BaseYqlTypeCompiler): | ||
def visit_uuid(self, type_: sa.Uuid, **kw): | ||
return "UTF8" | ||
|
||
def visit_UUID(self, type_: Union[sa.UUID, types.YqlUUID], **kw): | ||
return "UUID" | ||
|
||
def get_ydb_type( | ||
self, type_: sa.types.TypeEngine, is_optional: bool | ||
) -> Union[ydb.PrimitiveType, ydb.AbstractTypeBuilder]: | ||
if isinstance(type_, sa.TypeDecorator): | ||
type_ = type_.impl | ||
|
||
if isinstance(type_, sa.UUID): | ||
ydb_type = ydb.PrimitiveType.UUID | ||
if is_optional: | ||
return ydb.OptionalType(ydb_type) | ||
return ydb_type | ||
|
||
if isinstance(type_, sa.Uuid): | ||
ydb_type = ydb.PrimitiveType.Utf8 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Since sa.Uuid is subtype of sa.UUID this block is unreachable. I think we need to consider sa.Uuid also as ydb.PrimitiveType.UUID, because sqlalchemy is designed in this way:
Though it is not backward compatible for sure. |
||
if is_optional: | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -14,6 +14,28 @@ | |||||||||||||||||||||||||||
from .json import YqlJSON # noqa: F401 | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
class YqlUUID(types.UUID): | ||||||||||||||||||||||||||||
__visit_name__ = "UUID" | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
def bind_processor(self, dialect): | ||||||||||||||||||||||||||||
def process(value): | ||||||||||||||||||||||||||||
if value is None: | ||||||||||||||||||||||||||||
return None | ||||||||||||||||||||||||||||
if isinstance(value, str): | ||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||
import uuid as uuid_module | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
value = uuid_module.UUID(value) | ||||||||||||||||||||||||||||
Comment on lines
+26
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] Consider moving the
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump |
||||||||||||||||||||||||||||
except ValueError: | ||||||||||||||||||||||||||||
raise ValueError(f"Invalid UUID string: {value}") | ||||||||||||||||||||||||||||
return value | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
return process | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
def result_processor(self, dialect, coltype): | ||||||||||||||||||||||||||||
return None | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Why |
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
Comment on lines
+36
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider implementing a
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||||
class UInt64(types.Integer): | ||||||||||||||||||||||||||||
__visit_name__ = "uint64" | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Let's try to unskip uuid tests in suite by removing
@pytest.mark.skip("uuid unsupported for columns")