On Thu, 2005-07-07 at 12:56 +0200, Gerhard Haering wrote:
- is it true that after sqlite3_prepare, only the first call to
sqlite3_step can return SQLITE_BUSY, or can subsequent calls also
return SQLITE_BUSY?
Yes. I think this is true. At least I cannot think of any
path by which you could get SQLITE_BUSY on 2nd or subsequent
calls to sqlite3_step(). All the locks required are obtained
on the very first call.
Note however that a call to sqlite3_exec() is really divided up
into multiple calls to sqlite3_prepare() and sqlite3_step(), one
each for each complete SQL statement in the argument to
sqlite3_exec. So one statement could run to completion then a
second statement could hit a lock. But that cannot happen when
using sqlite3_prepare() directly because sqlite3_prepare() will
only compile one statement at a time.
- like described in the wiki page, is it true that if sqlite3_step
returns SQLITE_BUSY, I need to sqlite3_reset or sqlite3_finalize to
avoid potential deadlocks? If that's true, I will need to
sqlite3_reset, then rebind the parameters again and try sqlite3_step
again, right?
This is *not* correct. In fact, I do not see how calling
sqlite3_finalize or sqlite3_reset is even helpful in resolving
the lock issue. To avoid the deadlock, some thread needs to prepare
and run a COMMIT or ROLLBACK. Resetting or finalizing other
statements will not help.
A typical scenario follows. I use the term thread to mean either
a traditional thread or a full blown process - both work the same
way.
1. Threads A and B both execute BEGIN. This acquires no
locks or otherwise touches the database in any way. It just
turns off auto-commit mode in both threads.
2. Threads A and B both execute SELECT This acquires a
shared lock for both threads. This works fine.
3. Threads A and B both execute UPDATE One thread (let's
say thread A) succeeds, the other gets an SQLITE_BUSY reply.
The first thread gets a reserved lock before doing the update.
a reserved lock is a kind of exclusive lock so the other
thread blocks and has to fail. Thread A was successful at
the UPDATE command, but all the changes are still held in
RAM - nothing can be written to disk until thread B releases
its shared lock. If the UPDATE is large so that the changes
will not all fit in SQLite's page cache, then both threads
will get an SQLITE_BUSY return.
4. Thread A tries to COMMIT. It gets an SQLITE_BUSY return
because thread B is still holding a shared lock on the
database.
At this point we are stuck. Either thread A will need to
ROLLBACK to let thread B proceed. Or thread B will have to either
ROLLBACK or COMMIT (the same thing in this case since thread B has
made not changes) to let thread A proceed. One or the other of
the two threads will need to prepare and step a ROLLBACK. Just
resetting or finalizing previous statements and retrying them
will not help.
One way to avoid this scenario is to always use BEGIN EXCLUSIVE
instead of just BEGIN. BEGIN EXCLUSIVE always goes ahead and
gets an exclusive lock on the database file or returns SQLITE_BUSY
if it cannot. That way, you can never get two threads holding
a shared lock and both trying to promote to an exclusive lock.
This avoids the deadlock scenario described above, but it also
reduces the amount of concurrency. So it is a tradeoff.
--
D. Richard Hipp [EMAIL PROTECTED]