mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
* sqlite3: fix Blob.__setitem__ value range validation
Previously, assigning an out-of-range integer (negative or > 255) or an
integer too large for i64 (e.g. 2**65) to a Blob index raised OverflowError
instead of ValueError.
Mirror CPython's ass_subscript_index logic:
- Convert the value via to_i64(), treating any overflow as -1
- Validate the result is in [0, 255], raising ValueError("byte must be in range(0, 256)") otherwise
- Separate deletion error messages: "item deletion" for index, "slice deletion" for slice
* sqlite3: fix Blob.__setitem__ negative-step slice write
In the step != 1 branch of Blob.ass_subscript, the loop used
i_in_temp += step as usize
where step is isize. For negative steps (e.g. step = -2),
(-2isize) as usize = 18446744073709551614
causing an out-of-bounds panic whenever slice_len >= 2.
Fix: use SaturatedSliceIter (already used by the read path) to iterate
over the correct absolute blob indices, then map each index back to a
temp buffer offset via abs_idx - range_start.
Also fix a Clippy lint: replace
val < 0 || val > 255
with the idiomatic
!(0..=255).contains(&val)
Add a regression test in extra_tests/snippets/stdlib_sqlite.py that
exercises blob[9:0:-2] (negative step, slice_len=5).
* fix: guard blob negative-step snippet from CPython 3.11 bug
* style: add blank line after import sys in stdlib_sqlite snippet (ruff)
* Update extra_tests/snippets/stdlib_sqlite.py
---------
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
73 lines
1.9 KiB
Python
73 lines
1.9 KiB
Python
import sqlite3 as sqlite
|
|
import unittest
|
|
|
|
rows = [(3,), (4,)]
|
|
cx = sqlite.connect(":memory:")
|
|
cx.execute(";")
|
|
cx.executescript(";")
|
|
cx.execute("CREATE TABLE foo(key INTEGER)")
|
|
cx.executemany("INSERT INTO foo(key) VALUES (?)", rows)
|
|
|
|
cur = cx.cursor()
|
|
fetchall = cur.execute("SELECT * FROM foo").fetchall()
|
|
assert fetchall == rows
|
|
|
|
cx.executescript("""
|
|
/* CREATE TABLE foo(key INTEGER); */
|
|
INSERT INTO foo(key) VALUES (10);
|
|
INSERT INTO foo(key) VALUES (11);
|
|
""")
|
|
|
|
|
|
class AggrSum:
|
|
def __init__(self):
|
|
self.val = 0.0
|
|
|
|
def step(self, val):
|
|
self.val += val
|
|
|
|
def finalize(self):
|
|
return self.val
|
|
|
|
|
|
cx.create_aggregate("mysum", 1, AggrSum)
|
|
cur.execute("select mysum(key) from foo")
|
|
assert cur.fetchone()[0] == 28.0
|
|
|
|
# toobig = 2**64
|
|
# cur.execute("insert into foo(key) values (?)", (toobig,))
|
|
|
|
|
|
class AggrText:
|
|
def __init__(self):
|
|
self.txt = ""
|
|
|
|
def step(self, txt):
|
|
txt = str(txt)
|
|
self.txt = self.txt + txt
|
|
|
|
def finalize(self):
|
|
return self.txt
|
|
|
|
|
|
cx.create_aggregate("aggtxt", 1, AggrText)
|
|
cur.execute("select aggtxt(key) from foo")
|
|
assert cur.fetchone()[0] == "341011"
|
|
|
|
# Blob extended-slice assignment with negative step
|
|
# Guard: CPython 3.11 has a SystemError bug with negative-step Blob slicing;
|
|
# this test only runs on RustPython where the fix is being validated.
|
|
# TODO: remove this once https://github.com/python/cpython/pull/150450 is released and RustPython CI uses it.
|
|
import sys
|
|
|
|
if sys.implementation.name == "rustpython":
|
|
cx.execute("CREATE TABLE blobtest(b BLOB)")
|
|
data = b"this blob data string is exactly fifty bytes long!"
|
|
cx.execute("INSERT INTO blobtest(b) VALUES (?)", (data,))
|
|
blob = cx.blobopen("blobtest", "b", 1)
|
|
blob[9:0:-2] = b"12345" # writes to indices 9, 7, 5, 3, 1
|
|
actual = cx.execute("select b from blobtest").fetchone()[0]
|
|
expected = b"t5i4 3l2b1" + data[10:]
|
|
assert actual == expected, f"got {actual!r}, expected {expected!r}"
|
|
blob.close()
|