New submission from Luca Citi:

I have reported this bug to the pysqlite module for python2 ( 
https://github.com/ghaering/pysqlite/issues/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:

```bash
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:

```python
import sqlite3
con = sqlite3.connect('/tmp/test.db')
with con:                                                        
    con.execute("insert into person(firstname) values (?)", ("Jan",))
    pass
```

You should receive the following:

```
      1 with con:
      2         con.execute("insert into person(firstname) values (?)", 
("Jan",))
----> 3         pass
      4 

OperationalError: database is locked
```

Without exiting python, switch back to the first shell and kill the `'echo ... 
| sqlite3'` process. Then run:

```bash
sqlite3 /tmp/test.db .dump
```

you should get:

```
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
/**** ERROR: (5) database is locked *****/
ROLLBACK; -- due to errors
```

This means that the python process never executed a `rollback` and is still 
holding the lock. To release the lock one can exit python (clearly, this is not 
the intended behaviour of the context manager).

I believe the reason for this problem is that the exception happened in the 
implicit `commit` that is run on exiting the context manager, rather than 
inside it. In fact the exception is in the `pass` line rather than in the 
`execute` line. This exception did not trigger a `rollback` because the it 
happened after `pysqlite_connection_exit` checks for exceptions.

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.:

```bash
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 `execute` time rather than at `commit` 
time.

To fix this problem, I think the `pysqlite_connection_exit` function in 
src/connection.c should handle the case when the commit itself raises an 
exception, and invoke a rollback. Please see patch attached.

----------
components: Extension Modules
files: fix_pysqlite_connection_exit.patch
keywords: patch
messages: 268678
nosy: lciti
priority: normal
severity: normal
status: open
title: pysqlite3 context manager not performing rollback when a database is 
locked elsewhere for non-DML statements
versions: Python 3.6
Added file: http://bugs.python.org/file43420/fix_pysqlite_connection_exit.patch

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue27334>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to