* 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
* sqlite3: pass None for NULL authorizer args instead of crashing
CPython's authorizer callback receives NULL for arg1/arg2/db_name/access
when not applicable (e.g. SQLITE_READ on a table gives NULL for the
database name in some versions). Previously RustPython passed these
pointers to ptr_to_str which would crash or produce an error.
Now ptr_to_str_or_none is used: NULL pointers become Python None, which
matches CPython behavior and allows test_table_access and
test_column_access to pass.
Assisted-by: GitHub Copilot:claude-sonnet-4-6
* fix(sqlite3): avoid reentrant deadlock in Statement::new
sqlite3_prepare_v2 can synchronously invoke the authorizer callback,
which may call back into Connection methods (e.g. set_authorizer)
that require the same db lock. Holding db_lock across the prepare()
call caused a self-deadlock when a callback re-entered the connection.
Release the lock after sql_limit check and copy the raw handle before
calling prepare(), so FFI calls that can trigger Python re-entrancy
happen outside the lock scope.
Fixes hang in test_authorizer_concurrent_mutation_in_call
CPython 3.14 added check_num_params() which raises ProgrammingError when
narg/n_arg/num_params is out of range (-1..=SQLITE_LIMIT_FUNCTION_ARG).
RustPython passed invalid values directly to SQLite, resulting in an
OperationalError instead.
Add check_num_params() helper and call it in create_function(),
create_aggregate(), and create_window_function().
Assisted-by: GitHub Copilot:claude-sonnet-4-6
CPython raises ProgrammingError with "Binding N is a named parameter"
when a query uses named placeholders (, , ) but
the caller passes a sequence instead of a mapping.
RustPython's bind_parameters_sequence() did not check whether each
binding slot is a named parameter, so no error was raised.
Fix: call sqlite3_bind_parameter_name() for each slot in
bind_parameters_sequence(). If the first byte of the returned name is
not '?' (i.e. it is a named placeholder), raise ProgrammingError before
attempting to bind.
Assisted-by: GitHub Copilot:claude-sonnet-4-6
Row(cursor, data) raised ValueError when cursor.description was None.
CPython allows this case and returns an empty key list.
- Use empty tuple when description is None instead of raising
- Include the key name in the IndexError when a string key is not found
Assisted-by: GitHub Copilot:claude-sonnet-4-6
When a Connection is explicitly closed via con.close(), subsequent
operations (cursor(), commit(), rollback(), create_function(), etc.)
should raise ProgrammingError with 'Cannot operate on a closed database.'
to match CPython behaviour.
Previously, _db_lock() always returned 'Base Connection.__init__ not
called.' when self.db was None, without distinguishing between a
connection that was never initialised (subclass before __init__) and
one that was initialised and then explicitly closed.
Fix: inspect the initialized atomic flag — if True but db is None,
the connection was closed; if False, it was never initialised.
Assisted-by: GitHub Copilot:claude-sonnet-4-6
* 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>
Add #[pygetset] getter/setter for Cursor.row_factory so that Python-level
attribute access reads/writes the Rust struct field instead of the
instance dict.
Fix Connection.cursor() to only propagate the connection's row_factory
to the cursor when the connection's row_factory is not None, matching
CPython behavior. Previously it unconditionally overwrote the cursor's
row_factory, discarding any factory set by a cursor subclass __init__.
* Update sqlite3, dbm.sqlite3 and tests to CPython 3.14.2
* Add skip decorators for failing sqlite3 tests
Skip tests that fail due to unimplemented features or behavior differences:
- _iterdump not implemented (test_dump.py)
- Unraisable exception handling not implemented (test_hooks.py, test_userfunctions.py)
- Keyword-only arguments not supported for various methods
- Autocommit behavior differences (test_transactions.py)
- TransactionTests skipped due to timeout parameter type issue
- Various error message differences (test_dbapi.py)
- SQLITE_DBCONFIG constants not implemented
- Row and Connection signature inspection issues
All tests now pass with 95 skipped out of 493 total tests.
* Change @unittest.skip to @unittest.expectedFailure per code review
- Convert @unittest.skip decorators to @unittest.expectedFailure for tests that fail without panic/hang
- Keep @unittest.skip only for TransactionTests class (setUp fails with timeout=0 int type)
* fixup
Fix test_is_instance in CursorFactoryTests by properly handling the
factory argument in Connection.cursor() method. Now the factory can
be passed as both positional and keyword argument, and returns the
correct subclass type instead of always returning PyRef<Cursor>.
- Use FromArgs derive macro with CursorArgs struct for argument parsing
- Return PyObjectRef instead of PyRef<Cursor> to allow subclasses
- Use fast_issubclass to validate returned cursor is a Cursor subclass
- Properly differentiate between 'no argument' and 'None passed'
* Downgraded skips in tests
* Fixed failing tests
* Fixed test_ftplib + test_socket + test_ssl + test_threaded_import failures
* Removed comments about which tests are run in which environment
* Addressed PR comments
* Annotated skips on failing tests
* Removed unneeded tests
* Removed unneeded sys import from test_ftplib
* Added annotation to test_ftplib
* Readded skipIf to test_cleanup_with_symlink_modes with a more general ENV_POLLUTING_TESTS_WINDOWS
* Addressed PR comments
* Made changes to minimize diff in PR
* Apply suggestion from @youknowone
---------
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
* Fix sqlite3 Connection initialization check
Add proper __init__ validation for sqlite3.Connection to ensure base class
__init__ is called before using connection methods. This fixes the
test_connection_constructor_call_check test case.
Changes:
- Modified Connection.py_new to detect subclassing
- For base Connection class, initialization happens immediately in py_new
- For subclassed Connection, db is initialized as None
- Added __init__ method that performs actual database initialization
- Updated _db_lock error message to match CPython: 'Base Connection.__init__ not called.'
This ensures CPython compatibility where attempting to use a Connection
subclass instance without calling the base __init__ raises ProgrammingError.
* use Initializer trait
Add proper __init__ validation for sqlite3.Cursor to ensure base class
__init__ is called before using cursor methods. This fixes the
test_cursor_constructor_call_check test case.
Changes:
- Modified Cursor to initialize with inner=None in py_new
- Added explicit __init__ method that sets up CursorInner
- Updated close() method to check for uninitialized state
- Changed error message to match CPython: 'Base Cursor.__init__ not called.'
This ensures CPython compatibility where attempting to use a Cursor
instance without calling the base __init__ raises ProgrammingError.