Skip to content

Commit 704b5e5

Browse files
Reject non-positive Cursor.arraysize
ISSUE-33 — cursor.arraysize = 0 (or a negative number) used to make fetchmany() silently return [] because the implementation loops range(size). Users expect either an error or a sensible default; silent empty returns make this a debugging trap. Now the setter raises ProgrammingError on value < 1, matching the convention used by psycopg and asyncpg. Applied to both sync Cursor and AsyncCursor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f7876cb commit 704b5e5

File tree

3 files changed

+50
-0
lines changed

3 files changed

+50
-0
lines changed

src/dqlitedbapi/aio/cursor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ def arraysize(self) -> int:
5555

5656
@arraysize.setter
5757
def arraysize(self, value: int) -> None:
58+
if value < 1:
59+
from dqlitedbapi.exceptions import ProgrammingError
60+
61+
raise ProgrammingError(f"arraysize must be >= 1, got {value}")
5862
self._arraysize = value
5963

6064
def _check_closed(self) -> None:

src/dqlitedbapi/cursor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ def arraysize(self) -> int:
169169

170170
@arraysize.setter
171171
def arraysize(self, value: int) -> None:
172+
if value < 1:
173+
raise ProgrammingError(f"arraysize must be >= 1, got {value}")
172174
self._arraysize = value
173175

174176
def _check_closed(self) -> None:

tests/test_arraysize_validation.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Cursor.arraysize rejects non-positive values (ISSUE-33)."""
2+
3+
import pytest
4+
5+
from dqlitedbapi.aio.connection import AsyncConnection
6+
from dqlitedbapi.aio.cursor import AsyncCursor
7+
from dqlitedbapi.connection import Connection
8+
from dqlitedbapi.cursor import Cursor
9+
from dqlitedbapi.exceptions import ProgrammingError
10+
11+
12+
class TestArraysizeValidation:
13+
def _sync_cursor(self) -> Cursor:
14+
conn = Connection("localhost:19001", timeout=2.0)
15+
return Cursor(conn)
16+
17+
def _async_cursor(self) -> AsyncCursor:
18+
conn = AsyncConnection("localhost:19001")
19+
return AsyncCursor(conn)
20+
21+
def test_zero_rejected_sync(self) -> None:
22+
c = self._sync_cursor()
23+
with pytest.raises(ProgrammingError, match=">= 1"):
24+
c.arraysize = 0
25+
26+
def test_negative_rejected_sync(self) -> None:
27+
c = self._sync_cursor()
28+
with pytest.raises(ProgrammingError, match=">= 1"):
29+
c.arraysize = -5
30+
31+
def test_positive_accepted_sync(self) -> None:
32+
c = self._sync_cursor()
33+
c.arraysize = 10
34+
assert c.arraysize == 10
35+
36+
def test_zero_rejected_async(self) -> None:
37+
c = self._async_cursor()
38+
with pytest.raises(ProgrammingError, match=">= 1"):
39+
c.arraysize = 0
40+
41+
def test_negative_rejected_async(self) -> None:
42+
c = self._async_cursor()
43+
with pytest.raises(ProgrammingError, match=">= 1"):
44+
c.arraysize = -1

0 commit comments

Comments
 (0)