Skip to content

Commit 84584e6

Browse files
committed
Date32, Datetime64 and Timestamp64 support
1 parent 0842b5d commit 84584e6

File tree

6 files changed

+73
-2
lines changed

6 files changed

+73
-2
lines changed

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
sqlalchemy >= 1.4.0, < 3.0.0
2-
ydb >= 3.18.8
2+
ydb >= 3.21.6
33
ydb-dbapi >= 0.1.10

test/test_suite.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ctypes
2+
import datetime
23
import decimal
34

45
import pytest
@@ -424,6 +425,16 @@ class DateTest(_DateTest):
424425
run_dispose_bind = "once"
425426

426427

428+
class Date32Test(_DateTest):
429+
run_dispose_bind = "once"
430+
datatype = ydb_sa_types.YqlDate32
431+
data = datetime.date(1969, 1, 1)
432+
433+
@pytest.mark.skip("Default binding for DATE is not compatible with Date32")
434+
def test_select_direct(self, connection):
435+
pass
436+
437+
427438
class DateTimeMicrosecondsTest(_DateTimeMicrosecondsTest):
428439
run_dispose_bind = "once"
429440

@@ -432,10 +443,30 @@ class DateTimeTest(_DateTimeTest):
432443
run_dispose_bind = "once"
433444

434445

446+
class DateTime64Test(_DateTimeTest):
447+
datatype = ydb_sa_types.YqlDateTime64
448+
data = datetime.datetime(1969, 10, 15, 12, 57, 18)
449+
run_dispose_bind = "once"
450+
451+
@pytest.mark.skip("Default binding for DATETIME is not compatible with DateTime64")
452+
def test_select_direct(self, connection):
453+
pass
454+
455+
435456
class TimestampMicrosecondsTest(_TimestampMicrosecondsTest):
436457
run_dispose_bind = "once"
437458

438459

460+
class Timestamp64MicrosecondsTest(_TimestampMicrosecondsTest):
461+
run_dispose_bind = "once"
462+
datatype = ydb_sa_types.YqlTimestamp64
463+
data = datetime.datetime(1969, 10, 15, 12, 57, 18, 396)
464+
465+
@pytest.mark.skip("Default binding for TIMESTAMP is not compatible with Timestamp64")
466+
def test_select_direct(self, connection):
467+
pass
468+
469+
439470
@pytest.mark.skip("unsupported Time data type")
440471
class TimeTest(_TimeTest):
441472
pass

ydb_sqlalchemy/sqlalchemy/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def upsert(table):
6161
ydb.DecimalType: sa.DECIMAL,
6262
ydb.PrimitiveType.Yson: sa.TEXT,
6363
ydb.PrimitiveType.Date: sa.DATE,
64+
ydb.PrimitiveType.Date32: sa.DATE,
65+
ydb.PrimitiveType.Timestamp64: sa.TIMESTAMP,
66+
ydb.PrimitiveType.Datetime64: sa.DATETIME,
6467
ydb.PrimitiveType.Datetime: sa.DATETIME,
6568
ydb.PrimitiveType.Timestamp: sa.TIMESTAMP,
6669
ydb.PrimitiveType.Interval: sa.INTEGER,
@@ -437,6 +440,8 @@ def do_execute(
437440
) -> None:
438441
operation, parameters = self._prepare_ydb_query(statement, context, parameters, execute_many=False)
439442
is_ddl = context.isddl if context is not None else False
443+
print(operation)
444+
print(parameters)
440445
if is_ddl:
441446
cursor.execute_scheme(operation, parameters)
442447
else:

ydb_sqlalchemy/sqlalchemy/compiler/base.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ def visit_DATETIME(self, type_: sa.DATETIME, **kw):
135135
def visit_TIMESTAMP(self, type_: sa.TIMESTAMP, **kw):
136136
return "Timestamp"
137137

138+
def visit_date32(self, type_: types.YqlDate32, **kw):
139+
return "Date32"
140+
141+
def visit_timestamp64(self, type_: types.YqlTimestamp64, **kw):
142+
return "Timestamp64"
143+
144+
def visit_datetime64(self, type_: types.YqlDateTime64, **kw):
145+
return "DateTime64"
146+
138147
def visit_list_type(self, type_: types.ListType, **kw):
139148
inner = self.process(type_.item_type, **kw)
140149
return f"List<{inner}>"
@@ -193,6 +202,12 @@ def get_ydb_type(
193202
elif isinstance(type_, types.YqlJSON.YqlJSONPathType):
194203
ydb_type = ydb.PrimitiveType.Utf8
195204
# Json
205+
elif isinstance(type_, types.YqlDate32):
206+
ydb_type = ydb.PrimitiveType.Date32
207+
elif isinstance(type_, types.YqlTimestamp64):
208+
ydb_type = ydb.PrimitiveType.Timestamp64
209+
elif isinstance(type_, types.YqlDateTime64):
210+
ydb_type = ydb.PrimitiveType.Datetime64
196211
elif isinstance(type_, sa.DATETIME):
197212
ydb_type = ydb.PrimitiveType.Datetime
198213
elif isinstance(type_, sa.TIMESTAMP):

ydb_sqlalchemy/sqlalchemy/datetime_types.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,23 @@ def process(value: Optional[datetime.datetime]) -> Optional[int]:
3636
return int(value.timestamp())
3737

3838
return process
39+
40+
41+
class YqlDate32(YqlDate):
42+
__visit_name__ = "date32"
43+
44+
def literal_processor(self, dialect):
45+
parent = super().literal_processor(dialect)
46+
47+
def process(value):
48+
return f"Date32({parent(value)})"
49+
50+
return process
51+
52+
53+
class YqlTimestamp64(YqlTimestamp):
54+
__visit_name__ = "timestamp64"
55+
56+
57+
class YqlDateTime64(YqlDateTime):
58+
__visit_name__ = "datetime64"

ydb_sqlalchemy/sqlalchemy/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from sqlalchemy import ARRAY, exc, types
1212
from sqlalchemy.sql import type_api
1313

14-
from .datetime_types import YqlDate, YqlDateTime, YqlTimestamp # noqa: F401
14+
from .datetime_types import YqlDate, YqlDateTime, YqlTimestamp, YqlDate32, YqlTimestamp64, YqlDateTime64 # noqa: F401
1515
from .json import YqlJSON # noqa: F401
1616

1717

0 commit comments

Comments
 (0)