Skip to content

Commit 87ba91c

Browse files
committed
intervals support (closes #30)
1 parent 09fa5bc commit 87ba91c

26 files changed

+862
-250
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## v2.3.0
4+
**New features:**
5+
* Added support for [interval types](https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/interval_object/) [#30](https://github.com/igorcoding/asynctnt/issues/30)
6+
7+
38
## v2.2.0
49
**New features:**
510
* Implemented ability to send update/upsert requests with field names when schema is disabled (`fetch_schema=False`) and when fields are not found in the schema (good example of this case is using json path like `data.inner1.inner2.key1` as a key)

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ Documentation is available [here](https://igorcoding.github.io/asynctnt).
3434
* Full support for [SQL](https://www.tarantool.io/en/doc/latest/tutorials/sql_tutorial/),
3535
including [prepared statements](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_sql/prepare/).
3636
* Support for [interactive transaction](https://www.tarantool.io/en/doc/latest/book/box/atomic/txn_mode_mvcc/) via Tarantool streams.
37-
* Support of `Decimal`, `UUID` and `datetime` types natively.
37+
* Support of `Decimal`, `UUID`,`datetime` types natively.
38+
* Support for [interval types](https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/interval_object/).
3839
* Support for parsing [custom errors](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_error/new/).
3940
* **Schema fetching** on connection establishment, so you can use spaces and
4041
indexes names rather than their ids, and **auto refetching** if schema in

asynctnt/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
from .connection import Connection, connect
44
from .iproto.protocol import (
5+
Adjust,
56
Db,
67
Field,
78
IProtoError,
89
IProtoErrorStackFrame,
910
Iterator,
1011
Metadata,
12+
MPInterval,
1113
PushIterator,
1214
Response,
1315
Schema,
@@ -16,4 +18,4 @@
1618
TarantoolTuple,
1719
)
1820

19-
__version__ = "2.2.0"
21+
__version__ = "2.3.0"

asynctnt/iproto/buffer.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ cdef class WriteBuffer:
4444
cdef char *mp_encode_decimal(self, char *p, object value) except NULL
4545
cdef char *mp_encode_uuid(self, char *p, object value) except NULL
4646
cdef char *mp_encode_datetime(self, char *p, object value) except NULL
47+
cdef char *mp_encode_interval(self, char *p, MPInterval value) except NULL
4748
cdef char *mp_encode_array(self, char *p, uint32_t len) except NULL
4849
cdef char *mp_encode_map(self, char *p, uint32_t len) except NULL
4950
cdef char *mp_encode_list(self, char *p, list arr) except NULL

asynctnt/iproto/buffer.pyx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,20 @@ cdef class WriteBuffer:
279279
self._length += (p - begin)
280280
return p
281281

282+
cdef char *mp_encode_interval(self, char *p, MPInterval value) except NULL:
283+
cdef:
284+
char *begin
285+
char *data_p
286+
uint32_t length
287+
288+
length = interval_len(value)
289+
p = begin = self._ensure_allocated(p, mp_sizeof_ext(length))
290+
p = mp_encode_extl(p, tarantool.MP_INTERVAL, length)
291+
p = interval_encode(p, value)
292+
293+
self._length += (p - begin)
294+
return p
295+
282296
cdef char *mp_encode_array(self, char *p, uint32_t len) except NULL:
283297
cdef char *begin
284298
p = begin = self._ensure_allocated(p, mp_sizeof_array(len))
@@ -406,6 +420,9 @@ cdef class WriteBuffer:
406420
elif isinstance(o, datetime):
407421
return self.mp_encode_datetime(p, o)
408422

423+
elif isinstance(o, MPInterval):
424+
return self.mp_encode_interval(p, <MPInterval> o)
425+
409426
elif isinstance(o, Decimal):
410427
return self.mp_encode_decimal(p, o)
411428

asynctnt/iproto/cmsgpuck.pxd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ cdef extern from "../../third_party/msgpuck/msgpuck.h":
2626
cdef char *mp_store_u32(char *data, uint32_t val)
2727
cdef char *mp_store_u64(char *data, uint64_t val)
2828

29+
cdef ptrdiff_t mp_check_uint(const char *cur, const char *end)
30+
cdef ptrdiff_t mp_check_int(const char *cur, const char *end)
31+
2932
cdef mp_type mp_typeof(const char c)
3033

3134
cdef uint32_t mp_sizeof_array(uint32_t size)
@@ -43,6 +46,7 @@ cdef extern from "../../third_party/msgpuck/msgpuck.h":
4346
cdef uint32_t mp_sizeof_int(int64_t num)
4447
cdef char *mp_encode_int(char *data, int64_t num)
4548
cdef int64_t mp_decode_int(const char **data)
49+
cdef int mp_read_int64(const char **data, int64_t *ret)
4650

4751
cdef uint32_t mp_sizeof_float(float num)
4852
cdef char *mp_encode_float(char *data, float num)

asynctnt/iproto/ext.pxd

Lines changed: 0 additions & 32 deletions
This file was deleted.

asynctnt/iproto/ext/datetime.pxd

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from cpython.datetime cimport datetime
2+
from libc.stdint cimport int16_t, int32_t, int64_t, uint32_t
3+
4+
5+
cdef struct IProtoDateTime:
6+
int64_t seconds
7+
int32_t nsec
8+
int16_t tzoffset
9+
int16_t tzindex
10+
11+
cdef void datetime_zero(IProtoDateTime *dt)
12+
cdef uint32_t datetime_len(IProtoDateTime *dt)
13+
cdef char *datetime_encode(char *p, IProtoDateTime *dt) except NULL
14+
cdef int datetime_decode(const char ** p,
15+
uint32_t length,
16+
IProtoDateTime *dt) except -1
17+
cdef void datetime_from_py(datetime ob, IProtoDateTime *dt)
18+
cdef object datetime_to_py(IProtoDateTime *dt)

asynctnt/iproto/ext/datetime.pyx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
cimport cpython.datetime
2+
from cpython.datetime cimport PyDateTimeAPI, datetime, datetime_tzinfo, timedelta_new
3+
from libc.stdint cimport uint32_t
4+
from libc.string cimport memcpy
5+
6+
7+
cdef inline void datetime_zero(IProtoDateTime *dt):
8+
dt.seconds = 0
9+
dt.nsec = 0
10+
dt.tzoffset = 0
11+
dt.tzindex = 0
12+
13+
cdef inline uint32_t datetime_len(IProtoDateTime *dt):
14+
cdef uint32_t sz
15+
sz = sizeof(int64_t)
16+
if dt.nsec != 0 or dt.tzoffset != 0 or dt.tzindex != 0:
17+
return sz + DATETIME_TAIL_SZ
18+
return sz
19+
20+
cdef char *datetime_encode(char *p, IProtoDateTime *dt) except NULL:
21+
store_u64(p, dt.seconds)
22+
p += sizeof(dt.seconds)
23+
if dt.nsec != 0 or dt.tzoffset != 0 or dt.tzindex != 0:
24+
memcpy(p, &dt.nsec, DATETIME_TAIL_SZ)
25+
p += DATETIME_TAIL_SZ
26+
return p
27+
28+
cdef int datetime_decode(
29+
const char ** p,
30+
uint32_t length,
31+
IProtoDateTime *dt
32+
) except -1:
33+
delta = None
34+
tz = None
35+
36+
dt.seconds = load_u64(p[0])
37+
p[0] += sizeof(dt.seconds)
38+
length -= sizeof(dt.seconds)
39+
40+
if length == 0:
41+
return 0
42+
43+
if length != DATETIME_TAIL_SZ:
44+
raise ValueError("invalid datetime size. got {} extra bytes".format(
45+
length
46+
))
47+
48+
dt.nsec = load_u32(p[0])
49+
p[0] += 4
50+
dt.tzoffset = load_u16(p[0])
51+
p[0] += 2
52+
dt.tzindex = load_u16(p[0])
53+
p[0] += 2
54+
55+
cdef void datetime_from_py(datetime ob, IProtoDateTime *dt):
56+
cdef:
57+
double ts
58+
int offset
59+
ts = <double> ob.timestamp()
60+
dt.seconds = <int64_t> ts
61+
dt.nsec = <int32_t> ((ts - <double> dt.seconds) * 1000000) * 1000
62+
if dt.nsec < 0:
63+
# correction for negative dates
64+
dt.seconds -= 1
65+
dt.nsec += 1000000000
66+
67+
if datetime_tzinfo(ob) is not None:
68+
offset = ob.utcoffset().total_seconds()
69+
dt.tzoffset = <int16_t> (offset / 60)
70+
71+
cdef object datetime_to_py(IProtoDateTime *dt):
72+
cdef:
73+
double timestamp
74+
object tz
75+
76+
tz = None
77+
78+
if dt.tzoffset != 0:
79+
delta = timedelta_new(0, <int> dt.tzoffset * 60, 0)
80+
tz = timezone_new(delta)
81+
82+
timestamp = dt.seconds + (<double> dt.nsec) / 1e9
83+
return PyDateTimeAPI.DateTime_FromTimestamp(
84+
<PyObject *>PyDateTimeAPI.DateTimeType,
85+
(timestamp,) if tz is None else (timestamp, tz),
86+
NULL,
87+
)

asynctnt/iproto/ext/decimal.pxd

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from libc cimport math
2+
from libc.stdint cimport uint8_t, uint32_t
3+
4+
5+
cdef inline uint32_t bcd_len(uint32_t digits_len):
6+
return <uint32_t> math.floor(digits_len / 2) + 1
7+
8+
cdef uint32_t decimal_len(int exponent, uint32_t digits_count)
9+
cdef char *decimal_encode(char *p,
10+
uint32_t digits_count,
11+
uint8_t sign,
12+
tuple digits,
13+
int exponent) except NULL
14+
cdef object decimal_decode(const char ** p, uint32_t length)

0 commit comments

Comments
 (0)