From 74a4a51785540814f7bb72b50b381f7cc840ea84 Mon Sep 17 00:00:00 2001 From: Andrey Nehaychik Date: Sun, 9 Nov 2025 00:13:47 +0100 Subject: [PATCH 1/4] Prevent insertin None in non Nullable columns --- asynch/proto/columns/base.py | 3 ++ tests/test_cursors.py | 74 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) 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/test_cursors.py b/tests/test_cursors.py index c5288452..24744a99 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 @@ -113,6 +114,79 @@ 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 id,cnt,decimal,date,datetime,float,uuid,string,ipv4,ipv6,bool 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: + rows = await cursor.execute( + """INSERT INTO test.asynch(id,string) VALUES""", + [ + { + "id": 1, + "string": None, + } + ], + ) + assert False + except TypeMismatchError: + assert True + + try: + rows = await cursor.execute( + """INSERT INTO test.asynch(id,decimal) VALUES""", + [ + { + "id": 1, + "decimal": None, + } + ], + ) + assert False + except TypeMismatchError: + assert True + + try: + rows = 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): From 8ac335276abcb3a158129898622d77cbe06b1d99 Mon Sep 17 00:00:00 2001 From: Andrey Nehaychik Date: Sun, 9 Nov 2025 10:36:59 +0100 Subject: [PATCH 2/4] Fix tests --- tests/conftest.py | 21 +++++++++++++++++++++ tests/test_cursors.py | 5 +++-- 2 files changed, 24 insertions(+), 2 deletions(-) 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 24744a99..9263541b 100644 --- a/tests/test_cursors.py +++ b/tests/test_cursors.py @@ -118,7 +118,8 @@ async def test_insert_dict(conn: Connection): 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""", + """INSERT INTO test.asynch_nullable(""" + """id,cnt,decimal,date,datetime,float,uuid,string,ipv4,ipv6,bool) VALUES""", [ { "id": 1, @@ -137,7 +138,7 @@ async def test_nullable_insert_dict(conn: Connection): ) assert rows == 1 - await cursor.execute("SELECT id,cnt,decimal,date,datetime,float,uuid,string,ipv4,ipv6,bool FROM test.asynch_nullable") + 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 From 9560074e0087f47b84eb9ef63904e19b4e8ba6b4 Mon Sep 17 00:00:00 2001 From: Andrey Nehaychik Date: Sun, 9 Nov 2025 17:33:54 +0100 Subject: [PATCH 3/4] Fix formatting --- tests/test_cursors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_cursors.py b/tests/test_cursors.py index 9263541b..9e4dd47a 100644 --- a/tests/test_cursors.py +++ b/tests/test_cursors.py @@ -114,12 +114,13 @@ 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,cnt,decimal,date,datetime,float,uuid,string,ipv4,ipv6,bool) VALUES""", [ { "id": 1, @@ -143,6 +144,7 @@ async def test_nullable_insert_dict(conn: Connection): 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: From a9764ff37d3fb29b8d0f6feb731f6d4c95307a3f Mon Sep 17 00:00:00 2001 From: Andrey Nehaychik Date: Sun, 9 Nov 2025 17:42:14 +0100 Subject: [PATCH 4/4] Fix tests --- tests/test_cursors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cursors.py b/tests/test_cursors.py index 9e4dd47a..7b0b9456 100644 --- a/tests/test_cursors.py +++ b/tests/test_cursors.py @@ -149,7 +149,7 @@ async def test_nullable_insert_dict(conn: Connection): async def test_nullable_in_non_nullable_insert_dict(conn: Connection): async with conn.cursor(cursor=DictCursor) as cursor: try: - rows = await cursor.execute( + await cursor.execute( """INSERT INTO test.asynch(id,string) VALUES""", [ { @@ -163,7 +163,7 @@ async def test_nullable_in_non_nullable_insert_dict(conn: Connection): assert True try: - rows = await cursor.execute( + await cursor.execute( """INSERT INTO test.asynch(id,decimal) VALUES""", [ { @@ -177,7 +177,7 @@ async def test_nullable_in_non_nullable_insert_dict(conn: Connection): assert True try: - rows = await cursor.execute( + await cursor.execute( """INSERT INTO test.asynch(id,float) VALUES""", [ {