From: Mike Bayer Date: Thu, 21 Jan 2016 20:21:33 +0000 (-0500) Subject: - documenation updates to clarify specific SQLite versions X-Git-Tag: rel_1_1_0b1~84^2~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=89fa08792e98b9e31452aa3c949d9b909b10e7cd;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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 --- diff --git a/doc/build/changelog/migration_09.rst b/doc/build/changelog/migration_09.rst index b07aed9255..913815794f 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 a1786d16c7..c78723ee5f 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 d7dc9edc3f..ec9f24963d 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):