Skip to content

Commit a22da9e

Browse files
fix: defensive-copy _get_row_types row_types branch
`_get_row_types` returned `self.row_types[row_idx]` by reference on the per-row-types branch — the same aliasing pattern that issue 052 fixed for the `column_types` fallback. A caller who captured the return value and mutated it would silently rewrite the message's internal `row_types[i]` list. Return a fresh `list(...)` copy, completing the invariant that all three branches of `_get_row_types` now return independent lists. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d7221ff commit a22da9e

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

src/dqlitewire/messages/responses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def _get_row_types(self, row_idx: int, row: list[Any]) -> list[ValueType]:
254254
``__post_init__`` establishes (issue 042, issue 052).
255255
"""
256256
if self.row_types and row_idx < len(self.row_types):
257-
return self.row_types[row_idx]
257+
return list(self.row_types[row_idx])
258258
if self.column_types:
259259
return list(self.column_types)
260260
# Infer from values

tests/test_messages_responses.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,35 @@ def test_get_row_types_does_not_alias_column_types(self) -> None:
267267
"msg.column_types (issue 042 invariant)"
268268
)
269269

270+
def test_get_row_types_does_not_alias_row_types(self) -> None:
271+
"""Regression for issue 059.
272+
273+
The ``row_types`` branch of ``_get_row_types`` used to return
274+
``self.row_types[row_idx]`` by reference — the same aliasing
275+
pattern that issue 052 fixed for the ``column_types`` fallback.
276+
A caller who captured the return value and mutated it would
277+
silently rewrite the message's internal ``row_types[i]`` list.
278+
"""
279+
msg = RowsResponse(
280+
column_names=["x"],
281+
column_types=[ValueType.INTEGER],
282+
row_types=[[ValueType.INTEGER]],
283+
rows=[[1]],
284+
has_more=False,
285+
)
286+
287+
row_types = msg._get_row_types(0, [1])
288+
289+
assert row_types is not msg.row_types[0], (
290+
"_get_row_types must return a fresh list, not alias row_types[i]"
291+
)
292+
293+
row_types.append(ValueType.BLOB)
294+
assert msg.row_types[0] == [ValueType.INTEGER], (
295+
"mutating the _get_row_types return value must not affect "
296+
"msg.row_types[i] (issue 042 invariant)"
297+
)
298+
270299

271300
class TestRowsResponse:
272301
def test_empty_result(self) -> None:

0 commit comments

Comments
 (0)