Skip to content

Commit 7112da1

Browse files
authored
feat: Accept None as column values in Buffer.row() API. (#6)
1 parent 9d7ef9b commit 7112da1

File tree

5 files changed

+39
-20
lines changed

5 files changed

+39
-20
lines changed

DEV_NOTES.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Install your local Python3 environment **venv**
2828
venv/bin/python install -r dev_requirements.txt
2929
3030
# or simply:
31-
python3 proj.py venv
31+
./proj venv
3232
3333
# either of the ^ ^ should be followed by:
3434
source venv/bin/activate
@@ -80,15 +80,15 @@ No wheels are made.
8080

8181
.. code-block:: bash
8282
83-
python3 proj.py build
83+
./proj build
8484
8585
8686
Cleaning
8787
--------
8888

8989
.. code-block:: bash
9090
91-
python3 proj.py clean
91+
./proj clean
9292
9393
9494
Packaging Locally
@@ -99,8 +99,8 @@ MacOS Apple Silicon, run:
9999

100100
.. code-block:: bash
101101
102-
python3 proj.py sdist # source distribution
103-
python3 proj.py cibuildwheel # the order of these two lines does not matter
102+
./proj sdist # source distribution
103+
./proj cibuildwheel # the order of these two lines does not matter
104104
105105
This will end up putting everything in the ``dist/`` directory.
106106

@@ -239,4 +239,4 @@ In ``pyproject.toml``, add the following to the ``[tool.cibuildwheel]`` section:
239239
yum -y install valgrind
240240
"""
241241
242-
Note the ``gdb/commands.txt`` file. Review it and change it to fit your needs.
242+
Note the ``gdb/commands.txt`` file. Review it and change it to fit your needs.

proj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
python3 proj.py "$@"

proj.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
python3 proj.py $args

src/questdb/ingress.pyx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -635,14 +635,11 @@ cdef class Buffer:
635635
self._set_marker()
636636
try:
637637
self._table(table_name)
638-
if not (symbols or columns):
639-
raise IngressError(
640-
IngressErrorCode.InvalidApiCall,
641-
'Must specify at least one symbol or column')
642638
if symbols is not None:
643639
for name, value in symbols.items():
644-
self._symbol(name, value)
645-
wrote_fields = True
640+
if value is not None:
641+
self._symbol(name, value)
642+
wrote_fields = True
646643
if columns is not None:
647644
for name, value in columns.items():
648645
if value is not None:
@@ -663,7 +660,7 @@ cdef class Buffer:
663660
self,
664661
table_name: str,
665662
*,
666-
symbols: Optional[Dict[str, str]]=None,
663+
symbols: Optional[Dict[str, Optional[str]]]=None,
667664
columns: Optional[Dict[
668665
str,
669666
Union[None, bool, int, float, str, TimestampMicros, datetime]]
@@ -672,14 +669,12 @@ cdef class Buffer:
672669
"""
673670
Add a single row (line) to the buffer.
674671

675-
At least one ``symbols`` or ``columns`` must be specified.
676-
677672
.. code-block:: python
678673

679674
# All fields specified.
680675
buffer.row(
681676
'table_name',
682-
symbols={'sym1': 'abc', 'sym2': 'def'},
677+
symbols={'sym1': 'abc', 'sym2': 'def', 'sym3': None},
683678
columns={
684679
'col1': True,
685680
'col2': 123,
@@ -741,12 +736,16 @@ cdef class Buffer:
741736

742737
:param table_name: The name of the table to which the row belongs.
743738
:param symbols: A dictionary of symbol column names to ``str`` values.
739+
As a convenience, you can also pass a ``None`` value which will
740+
have the same effect as skipping the key: If the column already
741+
existed, it will be recorded as ``NULL``, otherwise it will not be
742+
created.
744743
:param columns: A dictionary of column names to ``bool``, ``int``,
745744
``float``, ``str``, ``TimestampMicros`` or ``datetime`` values.
746-
As a convenience, you can also pass a ``None`` value, however - due
747-
to ILP protocol limitations - this will skip the column rather
748-
necessarily writing a ``NULL`` value, so if the column did not exist
749-
yet it will not be created.
745+
As a convenience, you can also pass a ``None`` value which will
746+
have the same effect as skipping the key: If the column already
747+
existed, it will be recorded as ``NULL``, otherwise it will not be
748+
created.
750749
:param at: The timestamp of the row. If ``None``, timestamp is assigned
751750
by the server. If ``datetime``, the timestamp is converted to
752751
nanoseconds. A nanosecond unix epoch timestamp can be passed

test/test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ def test_column(self):
7575
'col5="val",col6=12345t,col7=7200000000t\n')
7676
self.assertEqual(str(buf), exp)
7777

78+
def test_none_symbol(self):
79+
buf = qi.Buffer()
80+
buf.row('tbl1', symbols={'sym1': 'val1', 'sym2': None})
81+
exp = 'tbl1,sym1=val1\n'
82+
self.assertEqual(str(buf), exp)
83+
self.assertEqual(len(buf), len(exp))
84+
85+
# No fields to write, no fields written, therefore a no-op.
86+
buf.row('tbl1', symbols={'sym1': None, 'sym2': None})
87+
self.assertEqual(str(buf), exp)
88+
self.assertEqual(len(buf), len(exp))
89+
7890
def test_none_column(self):
7991
buf = qi.Buffer()
8092
buf.row('tbl1', columns={'col1': 1})
@@ -87,6 +99,11 @@ def test_none_column(self):
8799
self.assertEqual(str(buf), exp)
88100
self.assertEqual(len(buf), len(exp))
89101

102+
def test_no_symbol_or_col_args(self):
103+
buf = qi.Buffer()
104+
buf.row('table_name')
105+
self.assertEqual(str(buf), '')
106+
90107
def test_unicode(self):
91108
buf = qi.Buffer()
92109
buf.row('tbl1', symbols={'questdb1': '❤️'}, columns={'questdb2': '❤️'})

0 commit comments

Comments
 (0)