summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-12-14 10:29:46 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2012-12-14 10:29:46 -0500
commit850e881ce2cadd1cac58e7e1dfa5e3fbb2db4faf (patch)
treebc0c2db35660365bce3d84c21e10e3658edbdaad
parentce3333a65c128d873a5c963a71d5c6961a42a3e8 (diff)
downloadsqlalchemy-850e881ce2cadd1cac58e7e1dfa5e3fbb2db4faf.tar.gz
More adjustment to this SQLite related issue which was released in
0.7.9, to intercept legacy SQLite quoting characters when reflecting foreign keys. In addition to intercepting double quotes, other quoting characters such as brackets, backticks, and single quotes are now also intercepted. [ticket:2568]
-rw-r--r--doc/build/changelog/changelog_07.rst10
-rw-r--r--doc/build/changelog/changelog_08.rst10
-rw-r--r--lib/sqlalchemy/dialects/sqlite/base.py67
-rw-r--r--test/dialect/test_sqlite.py28
4 files changed, 85 insertions, 30 deletions
diff --git a/doc/build/changelog/changelog_07.rst b/doc/build/changelog/changelog_07.rst
index d8e4e06ce..c778fb84b 100644
--- a/doc/build/changelog/changelog_07.rst
+++ b/doc/build/changelog/changelog_07.rst
@@ -9,6 +9,16 @@
:released:
.. change::
+ :tags: sqlite, bug
+ :tickets: 2568
+
+ More adjustment to this SQLite related issue which was released in
+ 0.7.9, to intercept legacy SQLite quoting characters when reflecting
+ foreign keys. In addition to intercepting double quotes, other
+ quoting characters such as brackets, backticks, and single quotes
+ are now also intercepted.
+
+ .. change::
:tags: sql, bug
:tickets: 2631
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst
index 61b7a5cc4..3b83cdb23 100644
--- a/doc/build/changelog/changelog_08.rst
+++ b/doc/build/changelog/changelog_08.rst
@@ -7,6 +7,16 @@
:version: 0.8.0b2
.. change::
+ :tags: sqlite, bug
+ :tickets: 2568
+
+ More adjustment to this SQLite related issue which was released in
+ 0.7.9, to intercept legacy SQLite quoting characters when reflecting
+ foreign keys. In addition to intercepting double quotes, other
+ quoting characters such as brackets, backticks, and single quotes
+ are now also intercepted. Also in 0.7.10.
+
+ .. change::
:tags: orm, bug
:tickets: 2635
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py
index f1be0385d..cd462021f 100644
--- a/lib/sqlalchemy/dialects/sqlite/base.py
+++ b/lib/sqlalchemy/dialects/sqlite/base.py
@@ -616,6 +616,8 @@ class SQLiteDialect(default.DefaultDialect):
supports_cast = True
supports_default_values = True
+ _broken_fk_pragma_quotes = False
+
def __init__(self, isolation_level=None, native_datetime=False, **kwargs):
default.DefaultDialect.__init__(self, **kwargs)
self.isolation_level = isolation_level
@@ -635,6 +637,12 @@ class SQLiteDialect(default.DefaultDialect):
self.dbapi.sqlite_version_info >= (3, 7, 11)
# http://www.sqlite.org/releaselog/3_7_11.html
+ # see http://www.sqlalchemy.org/trac/ticket/2568
+ # as well as http://www.sqlite.org/src/info/600482d161
+ self._broken_fk_pragma_quotes = \
+ self.dbapi.sqlite_version_info < (3, 6, 14)
+
+
_isolation_lookup = {
'READ UNCOMMITTED': 1,
'SERIALIZABLE': 0
@@ -851,37 +859,40 @@ class SQLiteDialect(default.DefaultDialect):
if row is None:
break
(numerical_id, rtbl, lcol, rcol) = (row[0], row[2], row[3], row[4])
- # sqlite won't return rcol if the table
- # was created with REFERENCES <tablename>, no col
- if rcol is None:
- rcol = lcol
-
- # see http://www.sqlalchemy.org/trac/ticket/2568
- # as well as http://www.sqlite.org/src/info/600482d161
- if self.dbapi.sqlite_version_info < (3, 6, 14):
- rtbl = re.sub(r'^\"|\"$', '', rtbl)
- try:
- fk = fks[numerical_id]
- except KeyError:
- fk = {
- 'name': None,
- 'constrained_columns': [],
- 'referred_schema': None,
- 'referred_table': rtbl,
- 'referred_columns': []
- }
- fkeys.append(fk)
- fks[numerical_id] = fk
-
- # look up the table based on the given table's engine, not 'self',
- # since it could be a ProxyEngine
- if lcol not in fk['constrained_columns']:
- fk['constrained_columns'].append(lcol)
- if rcol not in fk['referred_columns']:
- fk['referred_columns'].append(rcol)
+ self._parse_fk(fks, fkeys, numerical_id, rtbl, lcol, rcol)
return fkeys
+ def _parse_fk(self, fks, fkeys, numerical_id, rtbl, lcol, rcol):
+ # sqlite won't return rcol if the table
+ # was created with REFERENCES <tablename>, no col
+ if rcol is None:
+ rcol = lcol
+
+ if self._broken_fk_pragma_quotes:
+ rtbl = re.sub(r'^[\"\[`\']|[\"\]`\']$', '', rtbl)
+
+ try:
+ fk = fks[numerical_id]
+ except KeyError:
+ fk = {
+ 'name': None,
+ 'constrained_columns': [],
+ 'referred_schema': None,
+ 'referred_table': rtbl,
+ 'referred_columns': []
+ }
+ fkeys.append(fk)
+ fks[numerical_id] = fk
+
+ # look up the table based on the given table's engine, not 'self',
+ # since it could be a ProxyEngine
+ if lcol not in fk['constrained_columns']:
+ fk['constrained_columns'].append(lcol)
+ if rcol not in fk['referred_columns']:
+ fk['referred_columns'].append(rcol)
+ return fk
+
@reflection.cache
def get_indexes(self, connection, table_name, schema=None, **kw):
quote = self.identifier_preparer.quote_identifier
diff --git a/test/dialect/test_sqlite.py b/test/dialect/test_sqlite.py
index 96171f4f7..97962a54a 100644
--- a/test/dialect/test_sqlite.py
+++ b/test/dialect/test_sqlite.py
@@ -401,7 +401,7 @@ class DialectTest(fixtures.TestBase, AssertsExecutionResults):
meta.drop_all()
@testing.provide_metadata
- def test_quoted_identifiers_one(self):
+ def test_quoted_identifiers_functional_one(self):
"""Tests autoload of tables created with quoted column names."""
metadata = self.metadata
@@ -427,7 +427,7 @@ class DialectTest(fixtures.TestBase, AssertsExecutionResults):
== table2.c.id)
@testing.provide_metadata
- def test_quoted_identifiers_two(self):
+ def test_quoted_identifiers_functional_two(self):
""""test the edgiest of edge cases, quoted table/col names
that start and end with quotes.
@@ -462,6 +462,30 @@ class DialectTest(fixtures.TestBase, AssertsExecutionResults):
#assert j.onclause.compare(table1.c['"id"']
# == table2.c['"aid"'])
+ def test_legacy_quoted_identifiers_unit(self):
+ dialect = sqlite.dialect()
+ dialect._broken_fk_pragma_quotes = True
+
+
+ for row in [
+ (0, 'target', 'tid', 'id'),
+ (0, '"target"', 'tid', 'id'),
+ (0, '[target]', 'tid', 'id'),
+ (0, "'target'", 'tid', 'id'),
+ (0, '`target`', 'tid', 'id'),
+ ]:
+ fks = {}
+ fkeys = []
+ dialect._parse_fk(fks, fkeys, *row)
+ eq_(fkeys, [{
+ 'referred_table': 'target',
+ 'referred_columns': ['id'],
+ 'referred_schema': None,
+ 'name': None,
+ 'constrained_columns': ['tid']
+ }])
+
+
def test_attached_as_schema(self):
cx = testing.db.connect()
try: