Python DB API - commit() v. execute("commit transaction")?
Frank Millman
frank at chagford.com
Fri Jun 2 03:18:10 EDT 2017
"Skip Montanaro" wrote in message
news:CANc-5Uz2ruXRWnAX8pJEVQZtQbndC0aOJz3gGeb04k1ZfFfQgQ at mail.gmail.com...
> Assuming the underlying database supports transactions, is there any
difference between calling the commit() method on the connection and
calling the execute method on the cursor with the "commit transaction"
statement? It seems a bit asymmetric to me to start a transaction with
> cur.execute("begin transaction")
> but end it with
> conn.commit()
Yes there is a difference, at least as far as the combination of PostgreSQL
and psycopg2 is concerned. I will use 'PSQL' in the following, to save me
some typing.
A while ago I had to delve into PSQL locking, as I had a problem with locks
not being cleared. I learned that, for a simple SELECT statement, PSQL
checks to see if it is in a transaction. If not, it does not set any locks,
but if it is, it creates a lock which is cleared on the next
COMMIT/ROLLBACK.
By default, psycopg2 uses 'autocommit', which means that even a SELECT is
preceded by a 'BEGIN' statement internally. I never changed the default, so
all of the following assumes that autocommit is on.
I had many SELECT's, but I was not issuing any form of commit, so the locks
built up. I solved my problem by always committing. However in my
experimenting I found something curious.
I had one window open on a python session, where I could execute commands,
and another on a psql session, where I could monitor the 'lock' table.
I found that, if I issued a SELECT, a lock was created, if I called
conn.commit(), the lock was cleared. I could repeat this sequence and the
pattern was consistent.
However, if I issued a SELECT and called cur.execute('commit'), the lock was
cleared, but the next SELECT did *not* create a lock.
I worked out a possible reason for this, which I have not proved it by
examining the source code of psycopg2, but is internally consistent. The
theory goes like this -
psycopg2 is in one of two states - a transaction is active, or it is not
active. If you execute any command, and a transaction is not active, it
starts a transaction first. If you call conn.commit() or conn.rollback(), it
sends the command to the database and resets its state. However, (and this
is the theory,) if you call cur.execute('commit'), it sends the command to
the database, but does not reset its state. So when you execute the next
command, it thinks the transaction is still active, so it does not start a
new transaction. PSQL, on the other hand, knows that the previous
transaction has been committed, so if the next command is a SELECT, it does
not create a lock.
As I said, I cannot prove this, but the theory fits the observed behaviour
perfectly, so I have proceeded on the assumption that it is true. Therefore
I now always run every SQL command or block of commands within a context
manager, which always calls conn.commit() or conn.rollback() on exit, and I
have not had any more problems. I use exactly the same code for sqlite3 and
for Sql Server/pyodbc, and it has not caused any problems there either.
Frank Millman
More information about the Python-list
mailing list