Skip to content
Open
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
21 changes: 21 additions & 0 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,12 @@ def test_blob_get_slice_negative_index(self):
def test_blob_get_slice_with_skip(self):
self.assertEqual(self.blob[0:10:2], b"ti lb")

def test_blob_get_slice_with_negative_step(self):
# gh-150449: negative-step slices must not crash
self.assertEqual(self.blob[9:0:-2], self.data[9:0:-2])
self.assertEqual(self.blob[9::-2], self.data[9::-2])
self.assertEqual(self.blob[::-1], self.data[::-1])

def test_blob_set_slice(self):
self.blob[0:5] = b"12345"
expected = b"12345" + self.data[5:]
Expand All @@ -1406,6 +1412,21 @@ def test_blob_set_slice_with_skip(self):
expected = b"1h2s3b4o5 " + self.data[10:]
self.assertEqual(actual, expected)

def test_blob_set_slice_with_negative_step(self):
# gh-150449: negative-step slice assignment must not crash
expected = bytearray(self.data)
expected[9:0:-2] = b"12345"
self.blob[9:0:-2] = b"12345"
actual = self.cx.execute("select b from test").fetchone()[0]
self.assertEqual(actual, bytes(expected))

# Also verify a slice that includes index 0
expected2 = bytearray(self.data)
expected2[9::-2] = b"12345"
self.blob[9::-2] = b"12345"
actual2 = self.cx.execute("select b from test").fetchone()[0]
self.assertEqual(actual2, bytes(expected2))

def test_blob_mapping_invalid_index_type(self):
msg = "indices must be integers"
with self.assertRaisesRegex(TypeError, msg):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :class:`sqlite3.Blob` raising :exc:`SystemError` (or :exc:`ValueError`
on recent versions) when reading or writing with a negative-step slice such
as ``blob[9:0:-2]``.
33 changes: 25 additions & 8 deletions Modules/_sqlite/blob.c
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,14 @@ subscript_slice(pysqlite_Blob *self, PyObject *item)
return read_multiple(self, len, start);
}

PyObject *blob = read_multiple(self, stop - start, start);
// Compute the contiguous blob region covering all slice elements, then
// copy each element using the standard size_t-cursor pattern that handles
// both positive and negative steps via unsigned arithmetic.
Py_ssize_t last = start + (len - 1) * step;
Py_ssize_t read_offset = Py_MIN(start, last);
Py_ssize_t read_length = Py_ABS(start - last) + 1;

PyObject *blob = read_multiple(self, read_length, read_offset);
if (blob == NULL) {
return NULL;
}
Expand All @@ -456,10 +463,12 @@ subscript_slice(pysqlite_Blob *self, PyObject *item)
return NULL;
}
char *res_buf = PyBytesWriter_GetData(writer);

char *blob_buf = PyBytes_AS_STRING(blob);
for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
res_buf[i] = blob_buf[j];

size_t cur;
Py_ssize_t i;
for (cur = (size_t)start, i = 0; i < len; cur += (size_t)step, i++) {
res_buf[i] = blob_buf[(Py_ssize_t)cur - read_offset];
}
Py_DECREF(blob);
return PyBytesWriter_Finish(writer);
Expand Down Expand Up @@ -549,13 +558,21 @@ ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value)
rc = inner_write(self, vbuf.buf, len, start);
}
else {
PyObject *blob_bytes = read_multiple(self, stop - start, start);
// Compute the contiguous blob region covering all slice elements, then
// update each element using the standard size_t-cursor pattern that
// handles both positive and negative steps via unsigned arithmetic.
Py_ssize_t last = start + (len - 1) * step;
Py_ssize_t read_offset = Py_MIN(start, last);
Py_ssize_t read_length = Py_ABS(start - last) + 1;
PyObject *blob_bytes = read_multiple(self, read_length, read_offset);
if (blob_bytes != NULL) {
char *blob_buf = PyBytes_AS_STRING(blob_bytes);
for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
blob_buf[j] = ((char *)vbuf.buf)[i];
size_t cur;
Py_ssize_t i;
for (cur = (size_t)start, i = 0; i < len; cur += (size_t)step, i++) {
blob_buf[(Py_ssize_t)cur - read_offset] = ((char *)vbuf.buf)[i];
}
rc = inner_write(self, blob_buf, stop - start, start);
rc = inner_write(self, blob_buf, read_length, read_offset);
Py_DECREF(blob_bytes);
}
}
Expand Down
Loading