Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 23 additions & 7 deletions crates/stdlib/src/_sqlite3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2839,15 +2839,31 @@ mod _sqlite3 {
}
}

fn wrapped_index(index: PyIntRef, length: c_int, vm: &VirtualMachine) -> PyResult<c_int> {
let mut index = index.try_to_primitive::<c_int>(vm)?;
fn wrapped_index(
index: PyIntRef,
object: &PyObject,
length: c_int,
vm: &VirtualMachine,
) -> PyResult<c_int> {
let mut index = index.try_to_primitive::<isize>(vm).map_err(|_| {
let class = object.class();
let type_name = class.slot_name();
let mut end = type_name.len().min(200);
while !type_name.is_char_boundary(end) {
end -= 1;
}
vm.new_index_error(format!(
"cannot fit '{}' into an index-sized integer",
&type_name[..end]
))
})?;
if index < 0 {
index += length;
index += length as isize;
}
if index < 0 || index >= length {
if index < 0 || index >= length as isize {
Err(vm.new_index_error("Blob index out of range"))
} else {
Ok(index)
Ok(index as c_int)
}
}

Expand All @@ -2874,7 +2890,7 @@ mod _sqlite3 {
let inner = self.inner(vm)?;
if let Some(index) = needle.try_index_opt(vm) {
let blob_len = inner.blob.bytes();
let index = Self::wrapped_index(index?, blob_len, vm)?;
let index = Self::wrapped_index(index?, needle, blob_len, vm)?;
let mut byte: u8 = 0;
let ret = inner.blob.read_single(&mut byte, index);
self.check(ret, vm).map(|_| vm.ctx.new_int(byte).into())
Expand Down Expand Up @@ -2937,7 +2953,7 @@ mod _sqlite3 {
)));
};
let blob_len = inner.blob.bytes();
let index = Self::wrapped_index(index?, blob_len, vm)?;
let index = Self::wrapped_index(index?, needle, blob_len, vm)?;
// Mirror CPython ass_subscript_index: use PyLong_AsLong, treat any
// overflow (e.g. 2**65) as -1, then validate the [0, 255] range.
let val = int_val.as_bigint().to_i64().unwrap_or(-1);
Expand Down
56 changes: 56 additions & 0 deletions extra_tests/snippets/stdlib_sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,59 @@ def __del__(self):
"replace_callback",
):
check_nested_aggregate_destructor(operation)


class BlobIndex:
def __init__(self, value):
self.value = value

def __index__(self):
return self.value


class BlobInt(int):
pass


class BrokenBlobIndex:
def __index__(self):
raise OverflowError("index conversion failed")


cx.execute("CREATE TABLE blobindices(b BLOB)")
cx.execute("INSERT INTO blobindices(b) VALUES (?)", (b"abc",))
with cx.blobopen("blobindices", "b", 1) as blob:
assert blob[BlobIndex(-1)] == ord("c")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the prohibited snippet test edits

This newly added assertion block changes test logic in an existing snippet, but the root AGENTS.md marks such edits critical and says the only acceptable test-file changes are adding or removing expected-failure decorators and associated TODO comments. Remove this block from the commit instead of changing the snippet's assertions and test data.

AGENTS.md reference: AGENTS.md:L273-L279

Useful? React with 👍 / 👎.

blob[BlobIndex(0)] = ord("A")
assert blob[0] == ord("A")

test = unittest.TestCase()
for index in (sys.maxsize, -sys.maxsize - 1):
with test.assertRaisesRegex(IndexError, "^Blob index out of range$"):
blob[index]
with test.assertRaisesRegex(IndexError, "^Blob index out of range$"):
blob[index] = 0

for value in (sys.maxsize + 1, -sys.maxsize - 2):
for index in (value, BlobInt(value), BlobIndex(value)):
message = (
f"^cannot fit '{type(index).__name__}' into an index-sized integer$"
)
with test.assertRaisesRegex(IndexError, message):
blob[index]
with test.assertRaisesRegex(IndexError, message):
blob[index] = 0

for name, bounded_name in (("X" * 250, "X" * 200), ("€" * 67, "€" * 66)):
long_index = type(name, (), {"__index__": lambda self: sys.maxsize + 1})()
message = f"^cannot fit '{bounded_name}' into an index-sized integer$"
with test.assertRaisesRegex(IndexError, message):
blob[long_index]
with test.assertRaisesRegex(IndexError, message):
blob[long_index] = 0

with test.assertRaisesRegex(OverflowError, "^index conversion failed$"):
blob[BrokenBlobIndex()]
with test.assertRaisesRegex(OverflowError, "^index conversion failed$"):
blob[BrokenBlobIndex()] = 0
assert blob[:] == b"Abc"
Loading