Skip to content

Commit 7eb6d38

Browse files
authored
sqlite3: add SQLITE_DBCONFIG constants, setconfig() and getconfig() (RustPython#8535)
* sqlite3: add SQLITE_DBCONFIG constants, setconfig() and getconfig() Implement Connection.setconfig() and Connection.getconfig() using sqlite3_db_config(), and export all SQLITE_DBCONFIG_* integer constants to the module. Fixes test_connection_config in test_dbapi.py. Assisted-by: GitHub Copilot:claude-sonnet-4-6 * sqlite3: fix setconfig to propagate actual SQLite error When sqlite3_db_config() returns a non-OK result, propagate the actual SQLite error instead of replacing it with a generic 'Unable to set config' message, to match CPython's set_error_from_db() behavior. Assisted-by: GitHub Copilot:claude-sonnet-4-6
1 parent 70b47dd commit 7eb6d38

2 files changed

Lines changed: 77 additions & 7 deletions

File tree

Lib/test/test_sqlite3/test_dbapi.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,6 @@ def test_connection_bad_reinit(self):
525525
cx.executemany, "insert into t values(?)",
526526
((v,) for v in range(3)))
527527

528-
@unittest.expectedFailure # TODO: RUSTPYTHON; SQLITE_DBCONFIG constants not implemented
529528
def test_connection_config(self):
530529
op = sqlite.SQLITE_DBCONFIG_ENABLE_FKEY
531530
with memory_database() as cx:

crates/stdlib/src/_sqlite3.rs

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ mod _sqlite3 {
3131
sqlite3_column_double, sqlite3_column_int64, sqlite3_column_name, sqlite3_column_text,
3232
sqlite3_column_type, sqlite3_complete, sqlite3_context, sqlite3_context_db_handle,
3333
sqlite3_create_collation_v2, sqlite3_create_function_v2, sqlite3_create_window_function,
34-
sqlite3_data_count, sqlite3_db_handle, sqlite3_errcode, sqlite3_errmsg, sqlite3_exec,
35-
sqlite3_expanded_sql, sqlite3_extended_errcode, sqlite3_finalize, sqlite3_get_autocommit,
36-
sqlite3_interrupt, sqlite3_last_insert_rowid, sqlite3_libversion, sqlite3_limit,
37-
sqlite3_open_v2, sqlite3_prepare_v2, sqlite3_progress_handler, sqlite3_reset,
38-
sqlite3_result_blob, sqlite3_result_double, sqlite3_result_error,
34+
sqlite3_data_count, sqlite3_db_config, sqlite3_db_handle, sqlite3_errcode, sqlite3_errmsg,
35+
sqlite3_exec, sqlite3_expanded_sql, sqlite3_extended_errcode, sqlite3_finalize,
36+
sqlite3_get_autocommit, sqlite3_interrupt, sqlite3_last_insert_rowid, sqlite3_libversion,
37+
sqlite3_limit, sqlite3_open_v2, sqlite3_prepare_v2, sqlite3_progress_handler,
38+
sqlite3_reset, sqlite3_result_blob, sqlite3_result_double, sqlite3_result_error,
3939
sqlite3_result_error_nomem, sqlite3_result_error_toobig, sqlite3_result_int64,
4040
sqlite3_result_null, sqlite3_result_text, sqlite3_set_authorizer, sqlite3_sleep,
4141
sqlite3_step, sqlite3_stmt, sqlite3_stmt_busy, sqlite3_stmt_readonly, sqlite3_threadsafe,
@@ -161,7 +161,17 @@ mod _sqlite3 {
161161
SQLITE_ALTER_TABLE, SQLITE_ANALYZE, SQLITE_ATTACH, SQLITE_CREATE_INDEX,
162162
SQLITE_CREATE_TABLE, SQLITE_CREATE_TEMP_INDEX, SQLITE_CREATE_TEMP_TABLE,
163163
SQLITE_CREATE_TEMP_TRIGGER, SQLITE_CREATE_TEMP_VIEW, SQLITE_CREATE_TRIGGER,
164-
SQLITE_CREATE_VIEW, SQLITE_CREATE_VTABLE, SQLITE_DELETE, SQLITE_DENY, SQLITE_DETACH,
164+
SQLITE_CREATE_VIEW, SQLITE_CREATE_VTABLE, SQLITE_DBCONFIG_DEFENSIVE,
165+
SQLITE_DBCONFIG_DQS_DDL, SQLITE_DBCONFIG_DQS_DML, SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE,
166+
SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, SQLITE_DBCONFIG_ENABLE_COMMENTS,
167+
SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER,
168+
SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_DBCONFIG_ENABLE_QPSG,
169+
SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_DBCONFIG_ENABLE_VIEW,
170+
SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, SQLITE_DBCONFIG_LEGACY_FILE_FORMAT,
171+
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_DBCONFIG_RESET_DATABASE,
172+
SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_DBCONFIG_STMT_SCANSTATUS,
173+
SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_DBCONFIG_TRUSTED_SCHEMA,
174+
SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_DELETE, SQLITE_DENY, SQLITE_DETACH,
165175
SQLITE_DROP_INDEX, SQLITE_DROP_TABLE, SQLITE_DROP_TEMP_INDEX, SQLITE_DROP_TEMP_TABLE,
166176
SQLITE_DROP_TEMP_TRIGGER, SQLITE_DROP_TEMP_VIEW, SQLITE_DROP_TRIGGER, SQLITE_DROP_VIEW,
167177
SQLITE_DROP_VTABLE, SQLITE_FUNCTION, SQLITE_IGNORE, SQLITE_INSERT, SQLITE_LIMIT_ATTACHED,
@@ -1519,6 +1529,39 @@ mod _sqlite3 {
15191529
self.db_lock(vm)?.limit(category, limit, vm)
15201530
}
15211531

1532+
#[pymethod]
1533+
fn setconfig(
1534+
&self,
1535+
op: c_int,
1536+
enable: OptionalArg<bool>,
1537+
vm: &VirtualMachine,
1538+
) -> PyResult<()> {
1539+
let db = self.db_lock(vm)?;
1540+
if !is_int_dbconfig(op) {
1541+
return Err(vm.new_value_error(format!("unknown config 'op': {op}")));
1542+
}
1543+
let enable = enable.unwrap_or(true) as c_int;
1544+
let mut actual: c_int = 0;
1545+
let rc = unsafe { sqlite3_db_config(db.db, op, enable, &mut actual) };
1546+
db.check(rc, vm)?;
1547+
if enable != actual {
1548+
return Err(new_operational_error(vm, "Unable to set config".to_owned()));
1549+
}
1550+
Ok(())
1551+
}
1552+
1553+
#[pymethod]
1554+
fn getconfig(&self, op: c_int, vm: &VirtualMachine) -> PyResult<bool> {
1555+
let db = self.db_lock(vm)?;
1556+
if !is_int_dbconfig(op) {
1557+
return Err(vm.new_value_error(format!("unknown config 'op': {op}")));
1558+
}
1559+
let mut current: c_int = 0;
1560+
let rc = unsafe { sqlite3_db_config(db.db, op, -1, &mut current) };
1561+
db.check(rc, vm)?;
1562+
Ok(current != 0)
1563+
}
1564+
15221565
#[pymethod]
15231566
fn __enter__(zelf: PyRef<Self>) -> PyRef<Self> {
15241567
zelf
@@ -3537,6 +3580,34 @@ mod _sqlite3 {
35373580
Ok(())
35383581
}
35393582

3583+
fn is_int_dbconfig(op: c_int) -> bool {
3584+
use libsqlite3_sys::*;
3585+
matches!(
3586+
op,
3587+
SQLITE_DBCONFIG_ENABLE_FKEY
3588+
| SQLITE_DBCONFIG_ENABLE_TRIGGER
3589+
| SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
3590+
| SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION
3591+
| SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
3592+
| SQLITE_DBCONFIG_ENABLE_QPSG
3593+
| SQLITE_DBCONFIG_TRIGGER_EQP
3594+
| SQLITE_DBCONFIG_RESET_DATABASE
3595+
| SQLITE_DBCONFIG_DEFENSIVE
3596+
| SQLITE_DBCONFIG_WRITABLE_SCHEMA
3597+
| SQLITE_DBCONFIG_LEGACY_ALTER_TABLE
3598+
| SQLITE_DBCONFIG_DQS_DDL
3599+
| SQLITE_DBCONFIG_DQS_DML
3600+
| SQLITE_DBCONFIG_ENABLE_VIEW
3601+
| SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
3602+
| SQLITE_DBCONFIG_TRUSTED_SCHEMA
3603+
| SQLITE_DBCONFIG_STMT_SCANSTATUS
3604+
| SQLITE_DBCONFIG_REVERSE_SCANORDER
3605+
| SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
3606+
| SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
3607+
| SQLITE_DBCONFIG_ENABLE_COMMENTS
3608+
)
3609+
}
3610+
35403611
fn ptr_to_str<'a>(p: *const libc::c_char, vm: &VirtualMachine) -> PyResult<&'a str> {
35413612
if p.is_null() {
35423613
return Err(vm.new_memory_error("string pointer is null"));

0 commit comments

Comments
 (0)