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
6 changes: 0 additions & 6 deletions Lib/test/test_sqlite3/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ def test_authorizer_invalid_signature(self):
# Tests for checking that callback context mutations do not crash.
# Regression tests for https://github.com/python/cpython/issues/142830.

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'NoneType' object has no attribute 'exc_type'
@with_tracebacks(ZeroDivisionError, regex="hello world")
def test_authorizer_concurrent_mutation_in_call(self):
self.cx.execute("create table if not exists test(a number)")
Expand Down Expand Up @@ -263,7 +262,6 @@ def progress():
con.execute("select 1 union select 2 union select 3").fetchall()
self.assertEqual(action, 0, "progress handler was not cleared")

@unittest.expectedFailure # TODO: RUSTPYTHON
@with_tracebacks(ZeroDivisionError, msg_regex="bad_progress")
def test_error_in_progress_handler(self):
def bad_progress():
Expand All @@ -274,7 +272,6 @@ def bad_progress():
create table foo(a, b)
""")

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ZeroDivisionError, msg_regex="bad_progress")
def test_error_in_progress_handler_result(self):
class BadBool:
Expand Down Expand Up @@ -423,7 +420,6 @@ def test_trace_expanded_sql(self):
cx.execute("create table t(t)")
cx.executemany("insert into t values(?)", ((v,) for v in range(3)))

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(
sqlite.DataError,
regex="Expanded SQL string exceeds the maximum string length"
Expand All @@ -448,7 +444,6 @@ def test_trace_too_much_expanded_sql(self):
with self.check_stmt_trace(cx, [expanded_query]):
cx.execute(unexpanded_query, (ok_param,))

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ZeroDivisionError, regex="division by zero")
def test_trace_bad_handler(self):
with memory_database() as cx:
Expand All @@ -466,7 +461,6 @@ def test_set_trace_callback_keyword_args(self):
# but for the trace handler, exceptions are never re-raised (only
# printed when needed).

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'NoneType' object has no attribute 'exc_type'
@with_tracebacks(
TypeError,
regex=r".*<lambda>\(\) missing 6 required positional arguments",
Expand Down
16 changes: 0 additions & 16 deletions Lib/test/test_sqlite3/test_userfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ def test_func_return_nan(self):
cur.execute("select returnnan()")
self.assertIsNone(cur.fetchone()[0])

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ZeroDivisionError, msg_regex="func_raiseexception")
def test_func_exception(self):
cur = self.con.cursor()
Expand All @@ -263,15 +262,13 @@ def test_func_exception(self):
cur.fetchone()
self.assertEqual(str(cm.exception), 'user-defined function raised exception')

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(MemoryError, msg_regex="func_memoryerror")
def test_func_memory_error(self):
cur = self.con.cursor()
with self.assertRaises(MemoryError):
cur.execute("select memoryerror()")
cur.fetchone()

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(OverflowError, msg_regex="func_overflowerror")
def test_func_overflow_error(self):
cur = self.con.cursor()
Expand Down Expand Up @@ -305,7 +302,6 @@ def test_non_contiguous_blob(self):
self.con.execute, "select spam(?)",
(memoryview(b"blob")[::2],))

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(BufferError, regex="buffer.*contiguous")
def test_return_non_contiguous_blob(self):
with self.assertRaises(sqlite.OperationalError):
Expand Down Expand Up @@ -384,7 +380,6 @@ def md5sum(t):
del x,y
gc_collect()

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(OverflowError)
def test_func_return_too_large_int(self):
cur = self.con.cursor()
Expand All @@ -394,7 +389,6 @@ def test_func_return_too_large_int(self):
with self.assertRaisesRegex(sqlite.DataError, msg):
cur.execute("select largeint()")

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(UnicodeEncodeError, "surrogates not allowed")
def test_func_return_text_with_surrogates(self):
cur = self.con.cursor()
Expand Down Expand Up @@ -499,7 +493,6 @@ def test_win_error_on_create(self):
with self.assertRaisesRegex(sqlite.ProgrammingError, "not -100"):
self.con.create_window_function("shouldfail", -100, WindowSumInt)

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(BadWindow)
def test_win_exception_in_method(self):
for meth in "__init__", "step", "value", "inverse":
Expand All @@ -512,7 +505,6 @@ def test_win_exception_in_method(self):
self.cur.execute(self.query % name)
self.cur.fetchall()

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(BadWindow)
def test_win_exception_in_finalize(self):
# Note: SQLite does not (as of version 3.38.0) propagate finalize
Expand All @@ -524,7 +516,6 @@ def test_win_exception_in_finalize(self):
self.cur.execute(self.query % name)
self.cur.fetchall()

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(AttributeError)
def test_win_missing_method(self):
class MissingValue:
Expand Down Expand Up @@ -556,7 +547,6 @@ def finalize(self): return 42
self.cur.execute(self.query % name)
self.cur.fetchall()

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(AttributeError)
def test_win_missing_finalize(self):
# Note: SQLite does not (as of version 3.38.0) propagate finalize
Expand Down Expand Up @@ -633,7 +623,6 @@ def test_aggr_error_on_create(self):
with self.assertRaisesRegex(sqlite.ProgrammingError, "not -100"):
self.con.create_function("bla", -100, AggrSum)

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(AttributeError, msg_regex="AggrNoStep")
def test_aggr_no_step(self):
cur = self.con.cursor()
Expand All @@ -649,7 +638,6 @@ def test_aggr_no_finalize(self):
cur.execute("select nofinalize(t) from test")
val = cur.fetchone()[0]

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInInit")
def test_aggr_exception_in_init(self):
cur = self.con.cursor()
Expand All @@ -658,7 +646,6 @@ def test_aggr_exception_in_init(self):
val = cur.fetchone()[0]
self.assertEqual(str(cm.exception), "user-defined aggregate's '__init__' method raised error")

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInStep")
def test_aggr_exception_in_step(self):
cur = self.con.cursor()
Expand All @@ -667,7 +654,6 @@ def test_aggr_exception_in_step(self):
val = cur.fetchone()[0]
self.assertEqual(str(cm.exception), "user-defined aggregate's 'step' method raised error")

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInFinalize")
def test_aggr_exception_in_finalize(self):
cur = self.con.cursor()
Expand Down Expand Up @@ -797,12 +783,10 @@ def authorizer_cb(action, arg1, arg2, dbname, source):
raise ValueError
return sqlite.SQLITE_OK

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ValueError, msg_regex="authorizer_cb")
def test_table_access(self):
super().test_table_access()

@unittest.expectedFailure # TODO: RUSTPYTHON; unraisable exception handling not implemented
@with_tracebacks(ValueError, msg_regex="authorizer_cb")
def test_column_access(self):
super().test_table_access()
Expand Down
63 changes: 51 additions & 12 deletions crates/stdlib/src/_sqlite3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,26 @@ mod _sqlite3 {
rustpython_vm::vm::thread::with_vm(obj, |vm| f(obj, vm))
}

fn report_exception(callable: &PyObject, exc: PyBaseExceptionRef) {
if !enable_traceback().load(Ordering::Relaxed) {
return;
}
let callable = callable.to_owned();
// Both repr() and sys.unraisablehook can re-enter SQLite. The
// surrounding operation releases its cursor/statement locks first.
try_defer_drop(move || {
rustpython_vm::vm::thread::with_vm(&callable, |vm| {
let message = callable.repr(vm).ok().map(|repr| {
format!(
"Exception ignored on sqlite3 callback {}",
repr.to_string_lossy()
)
});
vm.run_unraisable(exc.clone(), message, vm.ctx.none());
});
});
}

// The SQLite mutex keeps the registration alive until this reference is acquired.
unsafe fn registered(data: *mut c_void) -> PyRc<Self> {
let data = data.cast::<Self>();
Expand Down Expand Up @@ -677,7 +697,10 @@ mod _sqlite3 {
val.try_to_primitive::<c_int>(vm)
};

f().unwrap_or(SQLITE_DENY)
f().unwrap_or_else(|exc| {
Self::report_exception(callable, exc);
SQLITE_DENY
})
})
.unwrap_or(SQLITE_DENY)
}
Expand All @@ -696,14 +719,30 @@ mod _sqlite3 {
unsafe { sqlite3_expanded_sql(stmt.cast()) },
|sql| unsafe { sqlite3_free(sql.cast()) },
);
let text =
ptr_to_str(*expanded, vm).or_else(|_| ptr_to_str(sql.cast(), vm))?;
let text = if expanded.is_null() {
let db = unsafe { sqlite3_db_handle(stmt.cast()) };
if unsafe { sqlite3_errcode(db) } == SQLITE_NOMEM {
return Err(vm.new_memory_error(""));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Self::report_exception(
callable,
vm.new_exception_msg(
data_error_type().to_owned(),
"Expanded SQL string exceeds the maximum string length".into(),
),
);
ptr_to_str(sql.cast(), vm)?
} else {
ptr_to_str(*expanded, vm)?
};
vm.ctx.new_str(text)
};
callable.call((stmt,), vm)?;
Ok(())
};
let _ = f();
if let Err(exc) = f() {
Self::report_exception(callable, exc);
}
0
})
.unwrap_or(0)
Expand All @@ -712,12 +751,13 @@ mod _sqlite3 {
unsafe extern "C" fn progress_callback(data: *mut c_void) -> c_int {
let data = unsafe { Self::registered(data) };
data.with_vm(|callable, vm| {
if let Ok(val) = callable.call((), vm)
&& let Ok(val) = val.is_true(vm)
{
return val as c_int;
match callable.call((), vm).and_then(|val| val.is_true(vm)) {
Ok(val) => val as c_int,
Err(exc) => {
Self::report_exception(callable, exc);
-1
}
}
-1
})
.unwrap_or(-1)
}
Expand Down Expand Up @@ -4441,9 +4481,8 @@ mod _sqlite3 {
} else {
unsafe { sqlite3_result_error(self.ctx, msg.as_ptr(), -1) }
}
if enable_traceback().load(Ordering::Relaxed) {
vm.print_exception(&exc);
}
let callable = unsafe { (*self.user_data::<CallbackData>()).obj.as_ref() };
CallbackData::report_exception(callable, exc);
}

fn db_handle(self) -> *mut sqlite3 {
Expand Down
Loading