Darren Duncan wrote:

I wasn't speaking in absolutes there, only in the context that if child transactions were implemented, and there were 2 ways to do it, then which way might have the least impact would be the way that most closely resembles SQLite's current behaviour.

Yes, I know. I re-read my last post this morning and it sounded a bit harsh, which is not what I intended. I'm sorry if I offended.

A transaction is just another name for a unit of work that is atomic, and each individual SQL statement *is* a transaction.

Therefore, SQLite right now already supports nested transactions to a limited degree, either 1 level (a SQL statement by itself) or 2 levels (a SQL statement within an explicit larger transaction). I propose it extend this to N levels.

I understand, but this extension is a; non-standard, and b; unnecessary. Additional levels can only include additional actions. If those actions must be completed atomically, then a single transaction is sufficient. If not, then they don't need to be in the same transaction.
If you have 2 layers of explicit transactions, and the child transaction rolls back, and the parent one retains its changes prior to when the child started, then this behaviour is identical to a partially completed SQL statement rolling back on failure to the beginning of that statement, and the results of prior executed statements remaining.

If you have executed a series of SQL statements without a transaction active, the changes due to each statement are committed immediately when it ends. If you need the series to be atomic, you enclose them in a transaction. If you haven't enclosed them in a transaction, then you are implicitly saying you don't require the last one to complete successfully for the first ones to be considered successful.

Proper child transaction support, where rollback of a child does not rollback the parent, is needed to deal with the reality that SQL statements are recursive, and any given SQL statement is itself defined in terms of other SQL statements, to arbitrary levels, and a SQL statement needs to be atomic no matter what level of abstraction you are at.

This simply isn't true. SQL statements are not recursive, and SQL statements are not defined in terms of other SQL statements.
This is best illustrated with a stored procedure. A stored procedure is a routine that contains one or more SQL statements. Moreover, the invocation of said stored procedure is also a SQL statement.

Yes it is, and if the invocation is not included in a larger transaction, the invocation statement will succeed or fail depending upon the result of the procedure. If the procedure completes successfully, the invocation was successful, and the invocation statement's implicit transaction is committed. However, if the invocation statement is one part of a larger transaction, then the changes are not committed until that larger transaction completes successfully. If the stored procedure fails, then the invocation statement will return an error. If the invocation statement fails, then the larger transaction can not be completed successfully, and so it should be rolled back.

If you think the prior statements in the larger transaction should be committed even if the stored procedure invocation statement fails, then you are really saying that the invocation statement should not be part of the transaction.

Therefore, both the individual SQL statements in the procedure plus the entire invocation of the stored procedure each need to be atomic, and hence a transaction.

If only the SQL statements inside the procedure were atomic, and the call to the procedure wasn't, then we can end up with a situation where, if the procedure fails and aborts part way through completion, then from the point of view of the users, the single SQL statement that they wrote (which invoked the procedure) did not complete, but did not leave the database untouched either, and hence left an inconsistent state. This violates ACID.

No, you are missing the point of a transaction. It's *all-or-nothing*. The individual statements in the stored procedure are not transactions. They are statements executed as part of the transaction that is opened implicitly by the invocation statement, or as parts of a previously opened transaction. They themselves will never open a transaction implicitly because one will always be open when they execute, either one open by the invocation statement, or one that was already open when the invocation statement is executed.

Or another example, say you have a trigger (an implicitly invoked procedure) defined to happen when a table is mutated (eg,ins/upd/del). From a user's point of view, everything that trigger does is part of the SQL statement that they invoked to mutate the table, and if the whole trigger isn't made atomic, then the user's ordinary SQL statement was not atomic from their POV.

The same mechanism applies to triggers. They are invoked by other SQL statements. Those invoking statements either open a transaction, or are part of a larger transaction.
Or if you want to ignore stored procedures, consider a SQL INSERT statement that inserts 1 row vs one that inserts multiple rows (some DBMSs support the latter). Say you want to insert multiple rows as an atomic unit, and you want to invoke a multi-row INSERT statement, but the DBMS doesn't provide one, so you have to invoke single-row INSERT statements instead. From your point of view, all the inserts combined are conceptually a single statement, that should be atomic in the same way that normal SQL statements are atomic.

So then you would open a transaction before the first insert, execute all your single row inserts, and then commit the transaction. It is the transaction that makes the series of inserts atomic.
A main advantage of child transaction support is that people can implement their own "SQL statements" and have them behave like system-defined ones, including the promise of atomicity.

This is a very real need, that should be supported.
You don't implement your own SQL statements, rather you implement sets or sequences of SQL statements. If your sequence must be atomic to work correctly, then you enclose them in a transaction.

Often users want to put the code that implements the sequence of SQL statements into a subroutine that they can call when ever that sequence is needed. The problem for users is that they cannot open another transaction when one is already open, and they don't want to prematurely close one when their routine is used as part of a larger transaction. This makes it difficult to call the subroutine without knowing the current transaction context. If there is an open transaction, then it shouldn't try to open another (because it will generate an error), but if there is not it should (assuming the set of operations must be atomic). Similarly, if the subroutine opened the transaction, it should close it, but it must not close the transaction if it did not open it, but rather simply executed within it..

These issues are handled correctly by counting the opens and pairing them off with closes until the first open it reached (which defines the beginning of the series that must be considered atomic), and the transaction is actually closed and the changes committed.

If one wants to still deny that rolling back a child without rolling back a parent has no practical use, then we might as well not have built-in SQL statements that are atomic, because that is exactly the same end result for users.

That is simply not true either. The atomicity is not a property of the SQL statement, it is a property of the transaction that the statement executes in (and they are always executed within a transaction, either and explicit one, or an implicit one).

I don't think I can say the same thing in many more ways. :-)

I am not saying there is *no* use for nested transactions or savepoints (which are the standard method of undoing parts of a transaction), just that they are not really as necessary or useful as most people assume when they first run into the single transaction issue. For *most* applications, a single transaction, and a set of functions that help subroutines do the right thing with respect to opening and closing the transaction, are all that is needed. I think this level of support is more in keeping with the minimalist nature of SQLite. Those few application that actually need that functionality may be better of using PostgreSQL, Oracle, or some other database engine that provides savepoints.

Dennis Cote



-----------------------------------------------------------------------------
To unsubscribe, send email to [EMAIL PROTECTED]
-----------------------------------------------------------------------------

Reply via email to