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 |