Skip to content

Commit 7800fd5

Browse files
authored
Merge pull request #35 from ydb-platform/add-support-for-timestamp-and-interval
add support for timestanp and interval
2 parents 5eddfc4 + 52c4b66 commit 7800fd5

File tree

3 files changed

+112
-5
lines changed

3 files changed

+112
-5
lines changed

tests/aio/test_types.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import pytest
2+
import ydb
3+
import datetime
4+
5+
6+
@pytest.mark.parametrize("enabled", [False, True])
7+
@pytest.mark.asyncio
8+
async def test_interval(driver, database, enabled):
9+
client = ydb.TableClient(
10+
driver, ydb.TableClientSettings().with_native_interval_in_result_sets(enabled)
11+
)
12+
session = await client.session().create()
13+
prepared = await session.prepare(
14+
"DECLARE $param as Interval;\n SELECT $param as value",
15+
)
16+
17+
param = datetime.timedelta(microseconds=-100) if enabled else -100
18+
result = await session.transaction().execute(
19+
prepared, {"$param": param}, commit_tx=True
20+
)
21+
assert result[0].rows[0].value == param
22+
23+
24+
@pytest.mark.parametrize("enabled", [False, True])
25+
@pytest.mark.asyncio
26+
async def test_timestamp(driver, database, enabled):
27+
client = ydb.TableClient(
28+
driver, ydb.TableClientSettings().with_native_timestamp_in_result_sets(enabled)
29+
)
30+
session = await client.session().create()
31+
prepared = await session.prepare(
32+
"DECLARE $param as Timestamp;\n SELECT $param as value",
33+
)
34+
35+
param = datetime.datetime.utcnow() if enabled else 100
36+
result = await session.transaction().execute(
37+
prepared, {"$param": param}, commit_tx=True
38+
)
39+
assert result[0].rows[0].value == param
40+
41+
result = await session.transaction().execute(
42+
prepared, {"$param": 1511789040123456}, commit_tx=True
43+
)
44+
if enabled:
45+
assert result[0].rows[0].value == datetime.datetime.fromisoformat(
46+
"2017-11-27 13:24:00.123456"
47+
)
48+
else:
49+
assert result[0].rows[0].value == 1511789040123456

ydb/table.py

+11
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,17 @@ def __init__(self):
997997
self._native_date_in_result_sets = False
998998
self._make_result_sets_lazy = False
999999
self._native_json_in_result_sets = False
1000+
self._native_interval_in_result_sets = False
1001+
1002+
def with_native_timestamp_in_result_sets(self, enabled):
1003+
# type:(bool) -> ydb.TableClientSettings
1004+
self._native_timestamp_in_result_sets = enabled
1005+
return self
1006+
1007+
def with_native_interval_in_result_sets(self, enabled):
1008+
# type:(bool) -> ydb.TableClientSettings
1009+
self._native_interval_in_result_sets = enabled
1010+
return self
10001011

10011012
def with_native_json_in_result_sets(self, enabled):
10021013
# type:(bool) -> ydb.TableClientSettings

ydb/types.py

+52-5
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import six
55
import json
66
from . import _utilities, _apis
7-
from datetime import date, datetime
7+
from datetime import date, datetime, timedelta
88
import uuid
99
import struct
1010
from google.protobuf import struct_pb2
1111

1212

13+
_SECONDS_IN_DAY = 60 * 60 * 24
14+
_EPOCH = datetime(1970, 1, 1)
1315
if six.PY3:
1416
_from_bytes = None
1517
else:
@@ -56,6 +58,42 @@ def _from_uuid(pb, value):
5658
pb.high_128 = struct.unpack("Q", value.bytes_le[8:16])[0]
5759

5860

61+
def _from_interval(value_pb, table_client_settings):
62+
if (
63+
table_client_settings is not None
64+
and table_client_settings._native_interval_in_result_sets
65+
):
66+
return timedelta(microseconds=value_pb.int64_value)
67+
return value_pb.int64_value
68+
69+
70+
def _timedelta_to_microseconds(value):
71+
return (value.days * _SECONDS_IN_DAY + value.seconds) * 1000000 + value.microseconds
72+
73+
74+
def _to_interval(pb, value):
75+
if isinstance(value, timedelta):
76+
pb.int64_value = _timedelta_to_microseconds(value)
77+
else:
78+
pb.int64_value = value
79+
80+
81+
def _from_timestamp(value_pb, table_client_settings):
82+
if (
83+
table_client_settings is not None
84+
and table_client_settings._native_timestamp_in_result_sets
85+
):
86+
return _EPOCH + timedelta(microseconds=value_pb.uint64_value)
87+
return value_pb.uint64_value
88+
89+
90+
def _to_timestamp(pb, value):
91+
if isinstance(value, datetime):
92+
pb.uint64_value = _timedelta_to_microseconds(value - _EPOCH)
93+
else:
94+
pb.uint64_value = value
95+
96+
5997
@enum.unique
6098
class PrimitiveType(enum.Enum):
6199
"""
@@ -81,8 +119,7 @@ class PrimitiveType(enum.Enum):
81119
Yson = _apis.primitive_types.YSON, "bytes_value"
82120
Json = _apis.primitive_types.JSON, "text_value", _from_json
83121
JsonDocument = _apis.primitive_types.JSON_DOCUMENT, "text_value", _from_json
84-
UUID = _apis.primitive_types.UUID, None, _to_uuid, _from_uuid
85-
122+
UUID = (_apis.primitive_types.UUID, None, _to_uuid, _from_uuid)
86123
Date = (
87124
_apis.primitive_types.DATE,
88125
"uint32_value",
@@ -93,8 +130,18 @@ class PrimitiveType(enum.Enum):
93130
"uint32_value",
94131
_from_datetime_number,
95132
)
96-
Timestamp = _apis.primitive_types.TIMESTAMP, "uint64_value"
97-
Interval = _apis.primitive_types.INTERVAL, "int64_value"
133+
Timestamp = (
134+
_apis.primitive_types.TIMESTAMP,
135+
None,
136+
_from_timestamp,
137+
_to_timestamp,
138+
)
139+
Interval = (
140+
_apis.primitive_types.INTERVAL,
141+
None,
142+
_from_interval,
143+
_to_interval,
144+
)
98145

99146
DyNumber = _apis.primitive_types.DYNUMBER, "text_value", _from_bytes
100147

0 commit comments

Comments
 (0)