Reputation: 1193
I am writing a simple database wrapper for sqlite as a learning experience. In doing so, I have been reading code for other, more complicated programs like Django
. In the django.db.backends.sqlite3.introspection
module, there is a method called get_primary_key_column
in the DatabaseIntrospection
class which is using re.search
to find the primary key:
def get_primary_key_column(self, cursor, table_name):
"""
Get the column name of the primary key for the given table.
"""
# Don't use PRAGMA because that causes issues with some transactions
cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s AND type = %s", [table_name, "table"])
row = cursor.fetchone()
if row is None:
raise ValueError("Table %s does not exist" % table_name)
results = row[0].strip()
results = results[results.index('(') + 1:results.rindex(')')]
for field_desc in results.split(','):
field_desc = field_desc.strip()
m = re.search('"(.*)".*PRIMARY KEY( AUTOINCREMENT)?$', field_desc)
if m:
return m.groups()[0]
return None
When I first saw this, I thought, "Now why don't they just use pragma table_info(foo)
and pick out the pk from the last items of the returned tuple set?" Then I saw the comment # Don't use PRAGMA because that causes issues with some transactions
which threw me for a loop. So my question is why not use the pragma
statement? what issues could that possibly cause?
Upvotes: 0
Views: 269
Reputation: 1124788
The sqlite3
library implicitly commits an active transaction when issuing a PRAGMA
statement and that caused issues with the Django tests.
See this entry on the original issue that introduced the comment:
Just to ensure nobody wastes any undue time on this, I've tracked down the problem with the SQLite implementation of this.
What's happening is check_constraints() is calling get_indexes() on the SQLite Introspection class. The implementation of get_indexes() executes a PRAGMA statement (a few actually).
From what I've been able to determine by testing a few things out, PRAGMA is the problem. It appears that calling PRAGMA is somehow committing the transaction prematurely. The result is that the fixtures being loaded here are being carried over test to test.
I can't find anything online about why PRAGMA statements would do this (or even whether they do). However, I did run these tests a bunch of times and the problem only arose once the PRAGMA statement was executed.
In the a.m. I can re-think this patch a bit and perhaps just focus on MySQL for the time being.
Emphasis mine; the Django devs found transaction issues during testing; the PRAGMA appeared to trigger a transaction commit.
It could be that the sqlite3
documentation was updated after those events; currently this behaviour is documented:
So if you are within a transaction and issue a command like
CREATE TABLE ...
,VACUUM
,PRAGMA
, thesqlite3
module will commit implicitly before executing that command. There are two reasons for doing that. The first is that some of these commands don’t work within transactions. The other reason is that sqlite3 needs to keep track of the transaction state (if a transaction is active or not).
Emphasis mine, again.
Upvotes: 2