summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2016-01-21 15:21:33 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2016-01-21 15:21:33 -0500
commit89fa08792e98b9e31452aa3c949d9b909b10e7cd (patch)
tree4c17c645fc7e61a8ba24d2360b0d2d09518a7ea4
parent963aa3029742b4f52082f5ea89fac2100130e15b (diff)
downloadsqlalchemy-89fa08792e98b9e31452aa3c949d9b909b10e7cd.tar.gz
- documenation updates to clarify specific SQLite versions
that have problems with right-nested joins and UNION column keys; references #3633 references #3634. backport from 1.1 to 0.9 announcing 1.1 as where these behaviors will be retired based on version-specific checks - fix test_resultset so that it passes when SQLite 3.10.0 is present, references #3633
-rw-r--r--doc/build/changelog/migration_09.rst8
-rw-r--r--lib/sqlalchemy/dialects/sqlite/base.py16
-rw-r--r--test/sql/test_resultset.py30
3 files changed, 41 insertions, 13 deletions
diff --git a/doc/build/changelog/migration_09.rst b/doc/build/changelog/migration_09.rst
index b07aed925..913815794 100644
--- a/doc/build/changelog/migration_09.rst
+++ b/doc/build/changelog/migration_09.rst
@@ -1125,7 +1125,7 @@ as INNER JOINs could always be flattened)::
SELECT a.*, b.*, c.* FROM a LEFT OUTER JOIN (b JOIN c ON b.id = c.id) ON a.id
-This was due to the fact that SQLite, even today, cannot parse a statement of the above format::
+This was due to the fact that SQLite up until version **3.7.16** cannot parse a statement of the above format::
SQLite version 3.7.15.2 2013-01-09 11:53:05
Enter ".help" for instructions
@@ -1248,6 +1248,12 @@ with the above queries rewritten as::
JOIN item ON item.id = order_item_1.item_id AND item.type IN (?)
) AS anon_1 ON "order".id = anon_1.order_item_1_order_id
+.. note::
+
+ As of SQLAlchemy 1.1, the workarounds present in this feature for SQLite
+ will automatically disable themselves when SQLite version **3.7.16**
+ or greater is detected, as SQLite has repaired support for right-nested joins.
+
The :meth:`.Join.alias`, :func:`.aliased` and :func:`.with_polymorphic` functions now
support a new argument, ``flat=True``, which is used to construct aliases of joined-table
entities without embedding into a SELECT. This flag is not on by default, to help with
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py
index a1786d16c..c78723ee5 100644
--- a/lib/sqlalchemy/dialects/sqlite/base.py
+++ b/lib/sqlalchemy/dialects/sqlite/base.py
@@ -358,8 +358,14 @@ Dotted Column Names
Using table or column names that explicitly have periods in them is
**not recommended**. While this is generally a bad idea for relational
databases in general, as the dot is a syntactically significant character,
-the SQLite driver has a bug which requires that SQLAlchemy filter out these
-dots in result sets.
+the SQLite driver up until version **3.10.0** of SQLite has a bug which
+requires that SQLAlchemy filter out these dots in result sets.
+
+.. note::
+
+ The following SQLite issue has been resolved as of version 3.10.0
+ of SQLite. SQLAlchemy as of **1.1** automatically disables its internal
+ workarounds based on detection of this version.
The bug, entirely outside of SQLAlchemy, can be illustrated thusly::
@@ -1000,6 +1006,9 @@ class SQLiteExecutionContext(default.DefaultExecutionContext):
return self.execution_options.get("sqlite_raw_colnames", False)
def _translate_colname(self, colname):
+ # TODO: detect SQLite version 3.10.0 or greater;
+ # see [ticket:3633]
+
# adjust for dotted column names. SQLite
# in the case of UNION may store col names as
# "tablename.colname", or if using an attached database,
@@ -1019,6 +1028,9 @@ class SQLiteDialect(default.DefaultDialect):
supports_empty_insert = False
supports_cast = True
supports_multivalues_insert = True
+
+ # TODO: detect version 3.7.16 or greater;
+ # see [ticket:3634]
supports_right_nested_joins = False
default_paramstyle = 'qmark'
diff --git a/test/sql/test_resultset.py b/test/sql/test_resultset.py
index d7dc9edc3..ec9f24963 100644
--- a/test/sql/test_resultset.py
+++ b/test/sql/test_resultset.py
@@ -318,7 +318,7 @@ class ResultProxyTest(fixtures.TablesTest):
dict(user_id=1, user_name='john'),
)
- # test a little sqlite weirdness - with the UNION,
+ # test a little sqlite < 3.10.0 weirdness - with the UNION,
# cols come back as "users.user_id" in cursor.description
r = testing.db.execute(
text(
@@ -332,7 +332,6 @@ class ResultProxyTest(fixtures.TablesTest):
eq_(r['user_name'], "john")
eq_(list(r.keys()), ["user_id", "user_name"])
- @testing.only_on("sqlite", "sqlite specific feature")
def test_column_accessor_sqlite_raw(self):
users = self.tables.users
@@ -347,13 +346,22 @@ class ResultProxyTest(fixtures.TablesTest):
"users.user_name from users",
bind=testing.db).execution_options(sqlite_raw_colnames=True). \
execute().first()
- not_in_('user_id', r)
- not_in_('user_name', r)
- eq_(r['users.user_id'], 1)
- eq_(r['users.user_name'], "john")
- eq_(list(r.keys()), ["users.user_id", "users.user_name"])
- @testing.only_on("sqlite", "sqlite specific feature")
+ if testing.against("sqlite < 3.10.0"):
+ not_in_('user_id', r)
+ not_in_('user_name', r)
+ eq_(r['users.user_id'], 1)
+ eq_(r['users.user_name'], "john")
+
+ eq_(list(r.keys()), ["users.user_id", "users.user_name"])
+ else:
+ not_in_('users.user_id', r)
+ not_in_('users.user_name', r)
+ eq_(r['user_id'], 1)
+ eq_(r['user_name'], "john")
+
+ eq_(list(r.keys()), ["user_id", "user_name"])
+
def test_column_accessor_sqlite_translated(self):
users = self.tables.users
@@ -369,8 +377,10 @@ class ResultProxyTest(fixtures.TablesTest):
bind=testing.db).execute().first()
eq_(r['user_id'], 1)
eq_(r['user_name'], "john")
- eq_(r['users.user_id'], 1)
- eq_(r['users.user_name'], "john")
+
+ if testing.against("sqlite < 3.10.0"):
+ eq_(r['users.user_id'], 1)
+ eq_(r['users.user_name'], "john")
eq_(list(r.keys()), ["user_id", "user_name"])
def test_column_accessor_labels_w_dots(self):