Friday, January 31, 2020, 1:14:26 AM, Peng Yu <pengyu...@gmail.com> wrote:

> I still have a hard time to understand what the difference is
> according to the python manual. It keeps saying see somewhere else in
> the python manual. But I don't see where it explains the differences
> between the differences comprehensively and clearly.

Let me offer another answer to go alongside Keith's excellent
response.


CORE SQLITE3 ENGINE

(Ignoring Python; as used from "C" or the command-line shell).

This operates in "auto-commit" mode. When a statement is executed that
is not already inside an EXPLICIT (=user-created) transaction, then it
is wrapped inside an IMPLICIT (=engine-created) transaction. Therefore:

   INSERT INTO TABLE_A ...
   BEGIN <whatever>
       INSERT INTO TABLE_B ...
       DELETE FROM TABLE_C ...
   COMMIT
   SELECT ... FROM TABLE_D

will essentially be turned into:

   BEGIN
       INSERT INTO TABLE_A ...
   COMMIT
   BEGIN <whatever>
       INSERT INTO TABLE_B ...
       DELETE FROM TABLE_C ...
   COMMIT
   BEGIN
       SELECT ... FROM TABLE_D
   COMMIT

where the auto-generated BEGINs are the equivalent of BEGIN DEFERRED
(the SQLite engine's default if you just use BEGIN).

To me, this behaviour is straightforward, predictable, and (mostly)
does what you (as a programmer) wants (but see the notes at the end).


PYTHON WRAPPER

Driven by what I assume were the best intentions, the Python wrapper
will, by default, try to be "helpful" and decide when IT will wrap
your SQL statements in BEGIN...COMMIT statements. How, and when it
does this is controlled by the ISOLATION_LEVEL.

If isolation_level is explicitly set to "None", then the Python
wrapper does nothing, and you get the "normal" behaviour of the core
SQLite3 engine as described above. As Keith recommends, and from what
I've seen on this mailing list, this is highly recommended when using
the Python wrapper. It makes everything done in Python work the same
way as in C or the command-line shell, with no unexpected surprises.

If you DON'T use isolation_level=None, then the Python wrapper will
enter what Keith calls "magic mode". It will SOMETIMES add its own
BEGIN ... COMMIT statements around your Python calls to SQLite.

By default (if you don't specify isolation_level) these added calls
will be simply BEGIN (which, the core SQLite engine treats as BEGIN
DEFERRED). Otherwise, you can explicitly specify an isolation_level
of DEFERRED (the same as leaving it blank), IMMEDIATE or EXCLUSIVE.

As Keith notes, the problem with letting Python insert these BEGIN
statements is that (a) different versions of the Python wrapper do
things in different ways, and (b) at least some of those ways "get
it wrong" (e.g. by not understanding things like WITH ... INSERT).

Overall, it seems far better to disable the Python wrapper's "magic"
mode (by using isolation_level=None) and have a far more predictable
environment where YOU manage transactions yourself.


FINAL NOTES

I said above that the auto-commit behaviour of the SQLite engine
"mostly" does what you want. One alternate behaviour would be to
insist that EVERY statement was inside an EXPLICIT transaction (and
raise an error if not). Most of the time, this would be somewhat
annoying (especially when using the command-line tool), but would
force the user/programmer to consciously think about when to use
transactions. For example, if you have:

    SELECT data1 FROM ...
    SELECT data2 FROM ...

then because each SELECT will be automatically wrapped inside their
own BEGIN ... COMMIT, it is entirely possible (="guaranteed to happen
the first time it is deployed in production") that some other process
will alter the data between the two SELECTs and you end up with
inconsistent values for "data1" and "data2". The answer, of course, is
to do:

    BEGIN [something]
        SELECT data1 FROM ...
        SELECT data2 FROM ...
    COMMIT

which guarantees consistency.

From my (moderate) use of SQL, (almost) always using explicit
transactions (rather than relying on the auto-commit behaviour of the
core) falls in to the same sort of list of  "good practices" as
explicitly declaring the columns in a SELECT statement (as opposed to
using SELECT *) and (depending on the programming language) always
using braces in an IF statement, even if there's only one statement
to conditionally execute. They're not essential, but tend to guard
against silly mistakes.


Regards,
Graham Holden


_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to