bzr branch
http://bzr.ed.am/sqlite3cc
| 
16
by edam
 - renamed database to connection to better identify what it is (would database_connection be better though?)  | 
1  | 
|
2  | 
DESIGN NOTES  | 
|
3  | 
============  | 
|
4  | 
||
5  | 
This file attempts to document the issues, pros and cons of various design  | 
|
6  | 
decisions that were made during the development of this library  | 
|
7  | 
||
8  | 
||
9  | 
Boost mutexes vs. SQLite mutexes  | 
|
10  | 
--------------------------------  | 
|
11  | 
||
12  | 
- Using boost's mutexes creates an additional dependency on a boost library  | 
|
13  | 
- With sqlite's mutexes, all statements play well together, even those that do  | 
|
14  | 
not explicitly use the database mutex (such as sqlite3 calls made by the  | 
|
15  | 
user from outside sqlite3cc)  | 
|
16  | 
||
17  | 
||
18  | 
Invalidating in-progress statements before rollback and commit  | 
|
19  | 
--------------------------------------------------------------  | 
|
20  | 
||
21  | 
The issue is that attempting to execute a ROLLBACK or COMMIT can sometimes  | 
|
22  | 
result in an error, depending on the state of other statements. Potentially,  | 
|
23  | 
rollbacks are done by the transaction_guard dtor when unwinding the stack in  | 
|
24  | 
response to an exception. Therefore, it is essential that they don't fail  | 
|
25  | 
because of outstanding statements as we cannot throw an additional exception.  | 
|
26  | 
||
27  | 
In practice, this issue only happens when queries, specifically, are still "in-  | 
|
28  | 
progress" (i.e., the previous call to sqlite3_step() for the statement resulted  | 
|
29  | 
in SQLITE_ROW being returned; before sqlite_step() is called and after  | 
|
30  | 
sqlite_step() returns SQLITE_DONE, statements are no longer "in-progress"). For  | 
|
31  | 
commands, there should be no results (SQLITE_ROW should never be returned) so  | 
|
32  | 
finalising the statement in command::step() is acceptable and guarantees that  | 
|
33  | 
commands will not be an issue.  | 
|
34  | 
||
35  | 
The errors caused:  | 
|
36  | 
- exec BEGIN, prep/step SELECT (returning row), prep/step on ROLLBACK gives  | 
|
37  | 
SQLITE_BUSY "database is locked"  | 
|
38  | 
- exec BEGIN, exec INSERT, prep/step SELECT (returning row), prep/step  | 
|
39  | 
ROLLBACK gives SQLITE_ERROR "SQL statements in progress"  | 
|
40  | 
||
41  | 
Possible solution is to reset queries during rollback and commit. This has a  | 
|
42  | 
danger associated with it that if the application is multi-threaded, we might  | 
|
43  | 
end up resetting a query from another thread. But then, if you're sharing a  | 
|
44  | 
database connection between threads and using transactions, you probably need  | 
|
45  | 
your head looking at. So, there are two ways we can do this:  | 
|
46  | 
||
47  | 
1. simply user sqlite3_next_stmt() to get outstanding statements and call  | 
|
48  | 
sqlite3_reset() on them  | 
|
49  | 
- this is an efficient solution, although not very OO.  | 
|
50  | 
||
51  | 
2. keep a map of active (i.e. those for whom step() last returned SQLITE_ROW)  | 
|
52  | 
queries in the database (using the sqlite3_stmt pointer as the key) and  | 
|
53  | 
iterate through asking queries to reset themselves.  | 
|
54  | 
- this is a more OO way of implementing it but has a greater overhead  |