-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[sqlite3] context manager leaves db locked if commit fails in __exit__ #71521
Comments
I have reported this bug to the pysqlite module for python2 ( ghaering/pysqlite#103 ) but I also report it here because it applies to python3 too. The pysqlite3 context manager does not perform a rollback when a transaction fails because the database is locked by some other process performing non-DML statements (e.g. during the sqlite3 command line .dump method). To reproduce the problem, open a terminal and run the following: sqlite3 /tmp/test.db 'drop table person; create table person (id integer primary key, firstname varchar)'
echo -e 'begin transaction;\nselect * from person;\n.system sleep 1000\nrollback;' | sqlite3 /tmp/test.db Leave this shell running and run the python3 interpreter from a different shell, then type: import sqlite3
con = sqlite3.connect('/tmp/test.db')
with con:
con.execute("insert into person(firstname) values (?)", ("Jan",))
pass You should receive the following:
Without exiting python, switch back to the first shell and kill the sqlite3 /tmp/test.db .dump you should get:
This means that the python process never executed a I believe the reason for this problem is that the exception happened in the implicit The expected behaviour (pysqlite3 rolling back and releasing the lock) is recovered if the initial blocking process is a Data Modification Language (DML) statement, e.g.: echo -e 'begin transaction; insert into person(firstname) values ("James");\n.system sleep 1000\nrollback;' | sqlite3 /tmp/test.db because this raises an exception at the To fix this problem, I think the |
You may also want to check that the method_name is "commit". A test case would nice to have. Note that the connection context manager will still fail cases like nested context managers. See bpo-16958. |
I can reproduce this without the I've taken the liberty to create a PR based on your patch, Luca. Berker's comments have been addressed in the PR. |
FYI, here's the SQLite API interaction from the context manager, chronologically (using the test from the PR). (I only show the relevant arguments passed to the API, for readability.) sqlite3_prepare_v2("insert into t values('test')", insert_stmt) => SQLITE_OK # Transaction control now begins # Here, the insert statement is executed
sqlite3_bind_blob_parameter_count(insert_stmt)
sqlite3_step(insert_stmt) => SQLITE_DONE
sqlite3_changes()
sqlite3_last_insert_rowid()
sqlite3_reset(insert_stmt) => SQLITE_OK
sqlite3_get_autocommit() # Enter __exit__: no exception has been raised, so it tries to commit # After the fix, rollback is now executed
sqlite3_prepare_v2("rollback", rollback_stmt)
sqlite3_step(rollback_stmt) => SQLITE_DONE
sqlite3_finalize(rollback_Stmt) As you can see, it does not fail (and raise an exception) until commit is issued inside __exit__. |
Thanks Luca, for the report, reproducer, and initial patch, Berker for helpful suggestion, and Łukasz, Pablo, & Victor for reviewing and merging. |
Nice! |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: