On Wed, Jan 7, 2015 at 11:47 PM, Keith Medcalf <kmedc...@dessus.com> wrote:
> On Wednesday, 7 January, 2015 20:01, James K. Lowden < > jklow...@schemamania.org> said: > >On Fri, 02 Jan 2015 21:41:02 -0700 > >"Keith Medcalf" <kmedc...@dessus.com> wrote: > >> On Friday, 2 January, 2015 16:26, James K. Lowden > >> <jklow...@schemamania.org> said: > >> > >> >On Thu, 25 Dec 2014 05:32:45 -0700 (MST) Rick Kelly > >> ><rpke...@gci.net> wrote: > >> > >> >> All SELECT type requests are wrapped with BEGIN TRANSACTION/COMMIT > >> > >> >That shouldn't be necessary and afaik isn't necessary. SELECT does > >> >not modify the database. To "commit a select" is to apply the > >> >nonchanges. > >> > >> It does not matter whether it modifies the database. "reading" the > >> database requires a lock -- a shared lock. "updating" the database > >> requires a "write" lock, which precludes obtaining a "shared" lock. > > > >When I first read your answer my reaction was, "yes, yes, of course". > >But I'm not sure where that leaves the OP. Are you suggesting select > >statements work "better" in some sense with autocommit turned off? > > No. However, when you perform an update in the same transaction (and > connection) as you are performing the select, when the select is not > completed yet, and you commit on the connection, it is to be expected that > AHWBL. > > >In passing I suggest *requires* is not true in general. It may be that > >SQLite and other implementations use locks to mediate access and > >implement ACID semantics. Locks are just one popular way to accomplish > >that, not the only one. > > >Your description of transaction implementation is illuminating. If I > >understand correctly, you're describing an odd set of > >design choices. > > > >> BEGIN TRANSACTION does not acquire a lock -- BEGIN IMMEDIATE does > >> that -- BEGIN TRANSACTION merely turns off autocommit, meaning that > >> the lock will not be released magically, but rather by an explicit > >> COMMIT (which itself does not do anything -- it merely turns > >> autocommit back on so that the next statement will commit the > >> transaction before magically acquiring a new lock). > > >I find this very peculiar. You aren't saying is that > > > > begin transaction; > > insert into T value (1); > > commit; > > [sqlite3_close] > > >leaves the database unchanged (because there's no "next statement")? > >If not, and there's a power outage between (successful) commit and > >closing the connection, what will be the state of the database on > >restart? Is the transaction still open or, if not, is it rolled > >forward and completed, or rolled back? > > I am not certain exactly where the COMMIT performs the commit. It could > very likely commit and then turn autocommit back on. Whatever so, it makes > no difference really. > > >> However, execution of a SELECT statement does cause a lock to be > >> obtained (a shared lock) and a COMMIT does cause that shared lock to > >> be released. > > >Again, I find this surprising. I would expect SELECT to > > > >1. establish a shared lock > >2. select the data > >3. release the lock > > Except that this is not what the OP is doing. The OP is performing a > COMMIT in the middle of the select running. He is doing this: > > 1. establish the lock > 2. select the first of one of many rows of data > 3. release the lock forcibly (COMMIT) > 4. read the next row of data from the select > 5. release the lock forcibly (COMMIT) > ... lather rinse repeat steps 4 and 5 until you reach the end of the > selected rows > > >whether or not BEGIN is called. If I understand what commit does per > >your description above, in a "transaction", the effect would be > > >1. BEGIN TRANSACTION (autocommit off) > >2. SELECT (take shared lock) > >3. data data data > >4. [SELECT done] (release shared lock) > >5. COMMIT (autocommit on) > > > >which leaves steps #1 and #5 redundant. > > >> Executing an UPDATE after a SELECT -- in the same connection -- (or > >> while a select is in progress) will escalate the SHARED lock to a > >> WRITE lock. > > >OK, this is typical. > > >> COMMIT will release "the lock" -- "the lock" is now a WRITE lock, not > >> a shared lock. Therefore the next _step() will be executing without > >> any lock at all leading to apparently undefined results (really an > >> error should be thrown "SQLITE_DATABASE_UNLOCKED_FROM_UNDER_ME" or a > >> MISUSE error, but is not). > > >Good to know. I hope we agree this is unlovely, and intersects with > >SQLite's unfortunate property of not isolating SELECT as atomic. (By > >which I mean: if there are 10 rows in a table and connection 1 issues > >"SELECT * FROM T" and between calls 4 & 5 to sqlite3_step another > >connection 2 issues "DELETE FROM T", the first process may get 4 or 5 > >rows, or 10, depending on luck of the draw.) > > No, it was the other persons choice to do this. He could have run the > select entirely, then done the updates. Or he could have chosen to do the > updates on another connection (which would be isolated from the connection > running the select) using WAL. > > >The right behavior is not a misuse error. How is it "misuse" for two > >connections to read and update the same table in overlapping time? > > It is not. There IS NOT TWO CONNECTIONS. There is only one connection. > The same connection is interspersing updates while running the select, and > attempting to commit the single connection in the middle of the select, but > still expecting that the select will "carry on". > -- The OP wrote: On Thu, Dec 25, 2014 at 7:32 AM, Rick Kelly <rpke...@gci.net> wrote: >*SNIP* > The database file is located in the same folder as the server. The server is > multi-threaded. I'm using library version 3.8.7.4 > The database file is opened as follows: > 1. sqlite3_enable_shared_cache(%SQLITE_OPEN_SHAREDCACHE) -- *Presumably* enabling SQLite Shared-Cache Mode likely signals his intention to HAVE two or more connections, right? If so, with that comes the Shared-Cache Locking Model at https://www.sqlite.org/sharedcache.html -- -- -- --Ô¿Ô-- K e V i N > > >The right behavior is to isolate SELECT from UPDATE. Let the reader see > >what was in the database at the time the SELECT was issued, unaffected > >by UPDATE. The COMMIT associated with UPDATE should not affect the > >SELECT's lock; rather it should pend until SELECT completes. Then its > >own lock acquires rights to the resources it needs, and is released when > >the work is done. > > You are correct. The way to do this is with journal_mode=WAL and two > separate connections, one for the select, and one for the update. > In the posited code, the COMMIT is not associated with the UPDATE, it is > associated with BOTH the SELECT and the UPDATE because they both occur on > the same connection. > > >> This is inherent in how WAL works. Just because WAL is not in effect > >> does not alter the fundamental workings of the transaction system. > > >Not sure how to parse that. I think you mean it's inherent in how > >transactions work, whether or not WAL is used? > > Without WAL, readers block writers and writers block readers. With WAL, > readers do not block writers -- they have ADDITIONAL processing that > isolates their view of the database to a point in time before the writer > obtained its lock. The implementation does not vary -- the basic > processing is the same -- WAL adds additional things, but does not change > the fact that a SELECT requires a shared lock -- it merely adds some > lookaside semantics. > > >> I do not believe that there is a way to specify "COMMIT BUT MAINTAIN > >> THE SHARED LOCK", (that is, to commit the changes only and > >> un-escalate the lock back to a shared lock) > > >No such syntax is needed if ACID semantics are respected. In SQL we > >we neither lock nor release anything, ever, explicitly. Locks are > >implementation artifacts. COMMIT simply means the data are safely > >stored. > > And that the "view state of the database is released". That is, in an > SQLite database you can do: > > BEGIN > SELECT ... > SELECT ... > SELECT ... > SELECT ... > COMMIT > > and the view of the database seen by this connection will be consistent > even though "some other process" modified the tables used in query 3 while > query 2 was executing. Even if those changes are commited by the writer > process, the above process will not see them until the COMMIT releases the > locks. I expect other databases do this as well. In fact I know that they > do, because you can perform steps like the above to do master/detail > processing and ensure consistent results, even though some other process > makes changes (or even deletes the whole master/detail shebang while query > 2 is executing. It is only if you try to update what is now non-existant > or changed rows from your view do you get an error that your data is stale. > > >As I said, your description (which I trust is accurate) is very helpful > >to someone who wants to understand how SQLite will act on the SQL > >provided to it. But it also protrays problematic choices that stray > >from SQL's defined behavior. > > Not really. It only shows that updating the database on the same > connection as is being used to run a select, and attempting to commit the > update before the select is complete results in chaos. I think you would > agree that the sequence: > > _prepare('select ...') > do while _step() != DONE > _step(_prepare('BEGIN')) > _step(_prepare('UPDATE ...')) > _step(_prepare('COMMIT')) > continue > > should not be valid. The BEGIN COMMIT should be moved outside the loop > unless the connection on which the select is processed is separate from > that on which the BEGIN/UPDATE/COMMIT is performed. > > > > > _______________________________________________ > sqlite-users mailing list > sqlite-users@sqlite.org > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users > _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users