Skip to content

Commit 1df85e1

Browse files
authored
fix: Python floats and ints are now transmitted with 64-bit precision instead of 32. (#14)
* fix: Python float now transmitted with 64-bit precision instead of 32. * fix: Integers are now 32-bit precision instead of 32. * fix: Directly accessing int64_t value from TimestampNanos/TimestampMicros instead of bridging through the Python int type.
1 parent 42f6984 commit 1df85e1

File tree

2 files changed

+56
-15
lines changed

2 files changed

+56
-15
lines changed

src/questdb/ingress.pyx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@
3030
API for fast data ingestion into QuestDB.
3131
"""
3232

33-
from libc.stdint cimport uint8_t, int64_t
33+
from libc.stdint cimport uint8_t, uint64_t, int64_t
3434
from cpython.datetime cimport datetime
3535
from cpython.bool cimport bool, PyBool_Check
3636
from cpython.weakref cimport PyWeakref_NewRef, PyWeakref_GetObject
3737
from cpython.object cimport PyObject
38+
from cpython.float cimport PyFloat_Check
39+
from cpython.int cimport PyInt_Check
40+
from cpython.unicode cimport PyUnicode_Check
3841

3942
from .line_sender cimport *
4043

@@ -518,16 +521,14 @@ cdef class Buffer:
518521
return 0
519522
520523
cdef inline int _column_i64(
521-
self, line_sender_column_name c_name, int value) except -1:
522-
# TODO: Generally audit for int overflows this in the whole codebase.
523-
# We pretty certainly have one here :-).
524+
self, line_sender_column_name c_name, int64_t value) except -1:
524525
cdef line_sender_error* err = NULL
525526
if not line_sender_buffer_column_i64(self._impl, c_name, value, &err):
526527
raise c_err_to_py(err)
527528
return 0
528529
529530
cdef inline int _column_f64(
530-
self, line_sender_column_name c_name, float value) except -1:
531+
self, line_sender_column_name c_name, double value) except -1:
531532
cdef line_sender_error* err = NULL
532533
if not line_sender_buffer_column_f64(self._impl, c_name, value, &err):
533534
raise c_err_to_py(err)
@@ -545,7 +546,7 @@ cdef class Buffer:
545546
cdef inline int _column_ts(
546547
self, line_sender_column_name c_name, TimestampMicros ts) except -1:
547548
cdef line_sender_error* err = NULL
548-
if not line_sender_buffer_column_ts(self._impl, c_name, ts.value, &err):
549+
if not line_sender_buffer_column_ts(self._impl, c_name, ts._value, &err):
549550
raise c_err_to_py(err)
550551
return 0
551552
@@ -562,11 +563,11 @@ cdef class Buffer:
562563
cdef bytes owner_name = str_to_column_name(name, &c_name)
563564
if PyBool_Check(value):
564565
return self._column_bool(c_name, value)
565-
elif isinstance(value, int):
566+
elif PyInt_Check(value):
566567
return self._column_i64(c_name, value)
567-
elif isinstance(value, float):
568+
elif PyFloat_Check(value):
568569
return self._column_f64(c_name, value)
569-
elif isinstance(value, str):
570+
elif PyUnicode_Check(value):
570571
return self._column_str(c_name, value)
571572
elif isinstance(value, TimestampMicros):
572573
return self._column_ts(c_name, value)
@@ -593,7 +594,7 @@ cdef class Buffer:
593594
594595
cdef inline int _at_ts(self, TimestampNanos ts) except -1:
595596
cdef line_sender_error* err = NULL
596-
if not line_sender_buffer_at(self._impl, ts.value, &err):
597+
if not line_sender_buffer_at(self._impl, ts._value, &err):
597598
raise c_err_to_py(err)
598599
return 0
599600
@@ -1078,9 +1079,9 @@ cdef class Sender:
10781079
str interface=None,
10791080
tuple auth=None,
10801081
object tls=False,
1081-
int read_timeout=15000,
1082-
int init_capacity=65536, # 64KiB
1083-
int max_name_len=127,
1082+
uint64_t read_timeout=15000,
1083+
uint64_t init_capacity=65536, # 64KiB
1084+
uint64_t max_name_len=127,
10841085
object auto_flush=64512): # 63KiB
10851086
cdef line_sender_error* err = NULL
10861087

@@ -1118,9 +1119,9 @@ cdef class Sender:
11181119
self._impl = NULL
11191120
self._buffer = None
11201121

1121-
if isinstance(port, int):
1122+
if PyInt_Check(port):
11221123
port_str = str(port)
1123-
elif isinstance(port, str):
1124+
elif PyUnicode_Check(port):
11241125
port_str = port
11251126
else:
11261127
raise TypeError(

test/test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,36 @@ def test_unicode(self):
109109
buf.row('tbl1', symbols={'questdb1': '❤️'}, columns={'questdb2': '❤️'})
110110
self.assertEqual(str(buf), 'tbl1,questdb1=❤️ questdb2="❤️"\n')
111111

112+
def test_float(self):
113+
buf = qi.Buffer()
114+
buf.row('tbl1', columns={'num': 1.2345678901234567})
115+
self.assertEqual(str(buf), f'tbl1 num=1.2345678901234567\n')
116+
117+
def test_int_range(self):
118+
buf = qi.Buffer()
119+
buf.row('tbl1', columns={'num': 0})
120+
self.assertEqual(str(buf), f'tbl1 num=0i\n')
121+
buf.clear()
122+
123+
# 32-bit int range.
124+
buf.row('tbl1', columns={'min': -2**31, 'max': 2**31-1})
125+
self.assertEqual(str(buf), f'tbl1 min=-2147483648i,max=2147483647i\n')
126+
buf.clear()
127+
128+
# 64-bit int range.
129+
buf.row('tbl1', columns={'min': -2**63, 'max': 2**63-1})
130+
self.assertEqual(str(buf), f'tbl1 min=-9223372036854775808i,max=9223372036854775807i\n')
131+
buf.clear()
132+
133+
# Overflow.
134+
with self.assertRaises(OverflowError):
135+
buf.row('tbl1', columns={'num': 2**63})
136+
137+
# Underflow.
138+
with self.assertRaises(OverflowError):
139+
buf.row('tbl1', columns={'num': -2**63-1})
140+
141+
112142

113143
class TestSender(unittest.TestCase):
114144
def test_basic(self):
@@ -343,6 +373,16 @@ def test_connect_after_close(self):
343373
with self.assertRaises(qi.IngressError):
344374
sender.connect()
345375

376+
def test_bad_init_args(self):
377+
with self.assertRaises(OverflowError):
378+
qi.Sender(host='localhost', port=9009, read_timeout=-1)
379+
380+
with self.assertRaises(OverflowError):
381+
qi.Sender(host='localhost', port=9009, init_capacity=-1)
382+
383+
with self.assertRaises(OverflowError):
384+
qi.Sender(host='localhost', port=9009, max_name_len=-1)
385+
346386

347387
if __name__ == '__main__':
348388
unittest.main()

0 commit comments

Comments
 (0)