Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
15e4092
FIX: Validate caller-controlled native buffer sizes
gargsaumya Sep 21, 2026
39dea1b
FIX: Update negative Arrow batch size test
gargsaumya Sep 21, 2026
0110fef
Merge branch 'main' into saumya/native-size-validation
gargsaumya Sep 21, 2026
a02fdf4
FIX: Address native size review findings
gargsaumya Sep 21, 2026
44982a9
Merge remote-tracking branch 'origin/saumya/native-size-validation' i…
gargsaumya Sep 21, 2026
7519aa4
Merge branch 'main' into saumya/native-size-validation
gargsaumya Sep 22, 2026
3038841
FIX: Bound native parameter and Arrow source buffers
gargsaumya Sep 22, 2026
710ff12
FIX: Address native size validation review
gargsaumya Sep 25, 2026
597d2c2
Merge remote-tracking branch 'origin/main' into saumya/native-size-va…
Copilot Sep 25, 2026
a096689
FIX: Close remaining native size validation gaps
gargsaumya Oct 7, 2026
231ae90
FIX: Measure override payloads in native units
gargsaumya Oct 7, 2026
f75aef7
Merge main into native size validation
gargsaumya Oct 7, 2026
444fb7f
FIX: Enforce peak native fetch memory limit
gargsaumya Oct 7, 2026
d5b4431
FIX: Bound streamed LOB native memory
gargsaumya Oct 7, 2026
3b60b8c
FIX: Validate executemany payload sizes
gargsaumya Oct 7, 2026
9f5c11a
FIX: Copy encoded wide text as bytes
gargsaumya Oct 7, 2026
5b26c36
FIX: Stream DAE payloads in bounded chunks
gargsaumya Oct 7, 2026
3a57124
FIX: Complete bounded DAE streaming
gargsaumya Oct 7, 2026
c64fbdd
FIX: Align DAE and array metadata
gargsaumya Oct 7, 2026
b8a8873
FIX: Satisfy strict DAE return analysis
gargsaumya Oct 7, 2026
9a55372
FIX: Close remaining native size gaps
gargsaumya Oct 7, 2026
7bb3a1a
FIX: Restore driver-compatible DAE binding
gargsaumya Oct 7, 2026
37ec527
FIX: Bound wide LOB conversion memory
gargsaumya Oct 7, 2026
7b5a3b5
FIX: Align streamed parameter metadata
gargsaumya Oct 7, 2026
190760f
FIX: Preserve MAX parameter type semantics
gargsaumya Oct 7, 2026
5d0a2e3
FIX: Size text by active C binding
gargsaumya Oct 7, 2026
e98d558
FIX: Bind NULL rows independently in DAE batches
gargsaumya Oct 7, 2026
99e837c
FIX: Validate row-local DAE tokens
gargsaumya Oct 7, 2026
a72841c
FIX: Use MAX sentinel for oversized DAE
gargsaumya Oct 7, 2026
5919670
FIX: Size DAE metadata through MAX boundary
gargsaumya Oct 7, 2026
db18317
FIX: align DAE metadata with wide bindings
gargsaumya Oct 7, 2026
400bd0f
FIX: preserve actual parameter metadata size
gargsaumya Oct 7, 2026
26c4763
FIX: address native validation review findings
gargsaumya Oct 7, 2026
f533230
FIX: reset DAE bindings after each row
gargsaumya Oct 7, 2026
48a2f23
FIX: restore geometric LOB buffer growth
gargsaumya Oct 8, 2026
d0912f6
FIX: bound LOB growth test helper
gargsaumya Oct 8, 2026
019a1cc
Merge branch 'main' into saumya/native-size-validation
gargsaumya Oct 12, 2026
3bd80c4
FIX: address remaining parameter sizing reviews
gargsaumya Oct 12, 2026
97100e0
FIX: initialize executemany DAE token
gargsaumya Oct 12, 2026
f63e905
FIX: complete DAE rows and bound numeric text
gargsaumya Oct 12, 2026
73ab6f8
FIX: use bounded Unicode encoding paths
gargsaumya Oct 12, 2026
8fdde26
FIX: size base Unicode contents in batches
gargsaumya Oct 12, 2026
d202fb6
Merge branch 'main' into saumya/native-size-validation
gargsaumya Oct 12, 2026
bf5e738
Merge branch 'main' into saumya/native-size-validation
gargsaumya Oct 16, 2026
748ceb2
FIX: Address native size validation review feedback
gargsaumya Oct 19, 2026
e3449ca
FIX: Isolate native parameter rejection tests
gargsaumya Oct 20, 2026
44017f9
FIX: Preserve Arrow rows across native buffer limits
gargsaumya Oct 20, 2026
79df605
FIX: Preserve Unicode in automatic executemany binding
gargsaumya Oct 22, 2026
2baafa6
FIX: Start binary LOB reads with a nonempty buffer
gargsaumya Oct 22, 2026
aba3271
FIX: Exercise Arrow pending rows beyond the native budget
gargsaumya Oct 22, 2026
6e9273e
Merge branch 'main' into saumya/native-size-validation
gargsaumya Oct 22, 2026
8fa595e
FIX: Validate DAE tokens and preserve bindings through error cleanup
gargsaumya Oct 22, 2026
16ca376
FIX: Invalidate pending Arrow rows for native autocommit attributes
gargsaumya Oct 26, 2026
47237cf
FIX: Enforce native input-size metadata limit
gargsaumya Oct 26, 2026
7c34a8c
Merge branch 'main' into saumya/native-size-validation
gargsaumya Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions mssql_python/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
SMALLMONEY_MAX: decimal.Decimal = decimal.Decimal("214748.3647")
MONEY_MIN: decimal.Decimal = decimal.Decimal("-922337203685477.5808")
MONEY_MAX: decimal.Decimal = decimal.Decimal("922337203685477.5807")
MAX_NATIVE_ROW_COUNT: int = 2_147_483_647
# SQL BIGINT is a signed 64-bit integer. Ints outside this range have no BIGINT
# encoding and must be rejected at detect time on both paths (see _map_sql_type).
BIGINT_MIN: int = -(2**63)
Expand Down Expand Up @@ -385,9 +386,7 @@ def __init__(self, connection: "Connection", timeout: int = 0) -> None:
]
] = None
self.rowcount: int = -1
self.arraysize: int = (
1 # Default number of rows to fetch at a time is 1, user can change it
)
self.arraysize = 1
self.buffer_length: int = 1024 # Default buffer length for string data
self._result_set_empty: bool = False # Add this initialization
self.last_executed_stmt: str = "" # Stores the last statement executed by this cursor
Expand Down Expand Up @@ -1136,6 +1135,25 @@ def _ensure_pyarrow(self) -> Any:
"pyarrow is required for Arrow fetch methods. Please install pyarrow."
) from e

@staticmethod
def _validate_native_row_count(value: int, name: str, allow_zero: bool) -> int:
minimum = 0 if allow_zero else 1
if isinstance(value, bool) or not isinstance(value, int):
raise TypeError(f"{name} must be an integer")
if value < minimum or value > MAX_NATIVE_ROW_COUNT:
Comment thread
gargsaumya marked this conversation as resolved.
raise ValueError(
f"{name} must be between {minimum} and {MAX_NATIVE_ROW_COUNT}, got {value}"
)
return value

@property
def arraysize(self) -> int:
return self._arraysize

@arraysize.setter
def arraysize(self, value: int) -> None:
self._arraysize = self._validate_native_row_count(value, "arraysize", allow_zero=False)

def setinputsizes(self, sizes: List[Union[int, tuple]]) -> None:
"""
Sets the type information to be used for parameters in execute and executemany.
Expand Down Expand Up @@ -1188,20 +1206,32 @@ def setinputsizes(self, sizes: List[Union[int, tuple]]) -> None:
sql_type, column_size, decimal_digits = size_info

# Validate SQL type
if not isinstance(sql_type, int) or sql_type not in valid_sql_types:
if (
isinstance(sql_type, bool)
or not isinstance(sql_type, int)
or sql_type not in valid_sql_types
):
raise ValueError(
f"Invalid SQL type: {sql_type}. Must be a valid SQL type constant."
)

sql_type = ODBC3_TEMPORAL_SQL_TYPES.get(sql_type, sql_type)

# Validate size and precision
if not isinstance(column_size, int) or column_size < 0:
if (
isinstance(column_size, bool)
or not isinstance(column_size, int)
or column_size < 0
):
Comment thread
gargsaumya marked this conversation as resolved.
raise ValueError(
f"Invalid column size: {column_size}. Must be a non-negative integer."
)

if not isinstance(decimal_digits, int) or decimal_digits < 0:
if (
isinstance(decimal_digits, bool)
or not isinstance(decimal_digits, int)
or decimal_digits < 0
):
raise ValueError(
f"Invalid decimal digits: {decimal_digits}. "
f"Must be a non-negative integer."
Expand All @@ -1220,7 +1250,11 @@ def setinputsizes(self, sizes: List[Union[int, tuple]]) -> None:
sql_type = size_info

# Validate SQL type
if not isinstance(sql_type, int) or sql_type not in valid_sql_types:
if (
isinstance(sql_type, bool)
or not isinstance(sql_type, int)
or sql_type not in valid_sql_types
):
raise ValueError(
f"Invalid SQL type: {sql_type}. Must be a valid SQL type constant."
)
Expand Down Expand Up @@ -2852,8 +2886,10 @@ def fetchmany(self, size: Optional[int] = None) -> List[Row]:

if size is None:
size = self.arraysize
else:
size = self._validate_native_row_count(size, "size", allow_zero=True)

if size <= 0:
if size == 0:
return []

char_decoding = self._get_decoding_settings(ddbc_sql_const.SQL_CHAR.value)
Expand Down Expand Up @@ -2989,16 +3025,15 @@ def arrow_batch(self, batch_size: int = 8192) -> "pyarrow.RecordBatch":
"""
self._check_closed() # Check if the cursor is closed
pyarrow = self._ensure_pyarrow()
batch_size = self._validate_native_row_count(batch_size, "batch_size", allow_zero=True)
Comment thread
gargsaumya marked this conversation as resolved.

if not self._has_result_set and self.description:
self._reset_rownumber()

capsules = []
char_decoding = self._get_decoding_settings(ddbc_sql_const.SQL_CHAR.value)
char_c_type = char_decoding.get("ctype", ddbc_sql_const.SQL_WCHAR.value)
ret = ddbc_bindings.DDBCSQLFetchArrowBatch(
self.hstmt, capsules, max(batch_size, 0), char_c_type
)
ret = ddbc_bindings.DDBCSQLFetchArrowBatch(self.hstmt, capsules, batch_size, char_c_type)
check_error(ddbc_sql_const.SQL_HANDLE_STMT.value, self.hstmt, ret)

batch = pyarrow.RecordBatch._import_from_c_capsule(*capsules)
Expand Down Expand Up @@ -3383,7 +3418,7 @@ def _bulkcopy_core_and_validate(table_name, batch_size, timeout):
logger.error("bulkcopy: Invalid table_name parameter")
raise ValueError("table_name must be a non-empty string")

if not isinstance(batch_size, int):
if not isinstance(batch_size, int) or isinstance(batch_size, bool):
raise TypeError(
f"batch_size must be a non-negative integer, got {type(batch_size).__name__}"
)
Comment thread
gargsaumya marked this conversation as resolved.
Expand Down
Loading
Loading