diff --git a/asynch/proto/columns/base.py b/asynch/proto/columns/base.py index e2fe5508..e8c46f68 100644 --- a/asynch/proto/columns/base.py +++ b/asynch/proto/columns/base.py @@ -90,6 +90,9 @@ def prepare_items(self, items): async def write_data(self, items): if self.nullable: await self._write_nulls_map(items) + else: + if None in items: + raise ColumnTypeMismatchException(None) await self._write_data(items) diff --git a/tests/conftest.py b/tests/conftest.py index c7c495fe..5cecc56a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -88,6 +88,26 @@ async def initialize_tests(): ORDER BY id """ ) + await cursor.execute( + """ + CREATE TABLE if not exists test.asynch_nullable + ( + `id` Int32, + `cnt` Nullable(Int32), + `decimal` Nullable(Decimal(10, 2)), + `date` Nullable(Date), + `datetime` Nullable(DateTime), + `float` Nullable(Float32), + `uuid` Nullable(UUID), + `string` Nullable(String), + `ipv4` Nullable(IPv4), + `ipv6` Nullable(IPv6), + `bool` Nullable(Bool) + ) + ENGINE = MergeTree + ORDER BY id + """ + ) yield @@ -96,6 +116,7 @@ async def truncate_table(): async with Connection(dsn=CONNECTION_DSN) as conn: async with conn.cursor(cursor=DictCursor) as cursor: await cursor.execute("truncate table test.asynch") + await cursor.execute("truncate table test.asynch_nullable") yield diff --git a/tests/test_cursors.py b/tests/test_cursors.py index c5288452..7b0b9456 100644 --- a/tests/test_cursors.py +++ b/tests/test_cursors.py @@ -4,6 +4,7 @@ from asynch.connection import Connection from asynch.cursors import DictCursor +from asynch.errors import TypeMismatchError from asynch.proto import constants @@ -114,6 +115,82 @@ async def test_insert_dict(conn: Connection): assert rows == 1 +@pytest.mark.asyncio +async def test_nullable_insert_dict(conn: Connection): + async with conn.cursor(cursor=DictCursor) as cursor: + rows = await cursor.execute( + """INSERT INTO test.asynch_nullable(""" + """id,cnt,decimal,date,datetime,float,uuid,string,ipv4,ipv6,bool) VALUES""", + [ + { + "id": 1, + "cnt": None, + "decimal": None, + "date": None, + "datetime": None, + "float": None, + "uuid": None, + "string": None, + "ipv4": None, + "ipv6": None, + "bool": None, + } + ], + ) + assert rows == 1 + + await cursor.execute("SELECT * FROM test.asynch_nullable") + result = await cursor.fetchone() + del result["id"] + assert len([item for item in result.values() if item is not None]) == 0 + + +@pytest.mark.asyncio +async def test_nullable_in_non_nullable_insert_dict(conn: Connection): + async with conn.cursor(cursor=DictCursor) as cursor: + try: + await cursor.execute( + """INSERT INTO test.asynch(id,string) VALUES""", + [ + { + "id": 1, + "string": None, + } + ], + ) + assert False + except TypeMismatchError: + assert True + + try: + await cursor.execute( + """INSERT INTO test.asynch(id,decimal) VALUES""", + [ + { + "id": 1, + "decimal": None, + } + ], + ) + assert False + except TypeMismatchError: + assert True + + try: + await cursor.execute( + """INSERT INTO test.asynch(id,float) VALUES""", + [ + { + "id": 1, + "float": None, + } + ], + ) + assert False + except TypeMismatchError: + assert True + + @pytest.mark.asyncio async def test_insert_tuple(conn: Connection): async with conn.cursor(cursor=DictCursor) as cursor: