diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-10-07 15:25:27 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-10-07 15:27:04 -0400 |
| commit | a0d94763f8369939ae6b731d6b599b5edb05d37e (patch) | |
| tree | 2a3232225d4f369ee16bf582d75cd3f7ab133e0a /lib/sqlalchemy | |
| parent | 744ef5382b80ce98294d621234141228c205825c (diff) | |
| download | sqlalchemy-a0d94763f8369939ae6b731d6b599b5edb05d37e.tar.gz | |
Drop right-nested join rewriting
Dropped support for right-nested join rewriting to support old SQLite
versions prior to 3.7.16, released in 2013. It is expected that
all modern Python versions among those now supported should all include
much newer versions of SQLite.
Fixes: #4895
Change-Id: I7f0cfc2b7d988ff147b9a4c6d5e2adec87e27029
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/dialects/sqlite/base.py | 12 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/sqlite/pysqlite.py | 45 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/default.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 158 |
4 files changed, 20 insertions, 199 deletions
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index defb64ec0..1bb7bd4fc 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -1467,9 +1467,15 @@ class SQLiteDialect(default.DefaultDialect): self.native_datetime = native_datetime if self.dbapi is not None: - self.supports_right_nested_joins = ( - self.dbapi.sqlite_version_info >= (3, 7, 16) - ) + if self.dbapi.sqlite_version_info < (3, 7, 16): + util.warn( + "SQLite version %s is older than 3.7.16, and will not " + "support right nested joins, as are sometimes used in " + "more complex ORM scenarios. SQLAlchemy 1.4 and above " + "no longer tries to rewrite these joins." + % (self.dbapi.sqlite_version_info,) + ) + self._broken_dotted_colnames = self.dbapi.sqlite_version_info < ( 3, 10, diff --git a/lib/sqlalchemy/dialects/sqlite/pysqlite.py b/lib/sqlalchemy/dialects/sqlite/pysqlite.py index 4eca7ae31..89254eef1 100644 --- a/lib/sqlalchemy/dialects/sqlite/pysqlite.py +++ b/lib/sqlalchemy/dialects/sqlite/pysqlite.py @@ -18,19 +18,8 @@ r""" Driver ------ -When using Python 2.5 and above, the built in ``sqlite3`` driver is -already installed and no additional installation is needed. Otherwise, -the ``pysqlite2`` driver needs to be present. This is the same driver as -``sqlite3``, just with a different name. - -The ``pysqlite2`` driver will be loaded first, and if not found, ``sqlite3`` -is loaded. This allows an explicitly installed pysqlite driver to take -precedence over the built in one. As with all dialects, a specific -DBAPI module may be provided to :func:`~sqlalchemy.create_engine()` to control -this explicitly:: - - from sqlite3 import dbapi2 as sqlite - e = create_engine('sqlite+pysqlite:///file.db', module=sqlite) +The ``sqlite3`` Python DBAPI is standard on all modern Python versions; +for cPython and Pypy, no additional installation is necessary. Connect Strings @@ -379,30 +368,18 @@ class SQLiteDialect_pysqlite(SQLiteDialect): driver = "pysqlite" - def __init__(self, uri=False, **kwargs): - SQLiteDialect.__init__(self, **kwargs) - - if self.dbapi is not None: - sqlite_ver = self.dbapi.version_info - if sqlite_ver < (2, 1, 3): - util.warn( - ( - "The installed version of pysqlite2 (%s) is out-dated " - "and will cause errors in some cases. Version 2.1.3 " - "or greater is recommended." - ) - % ".".join([str(subver) for subver in sqlite_ver]) - ) - @classmethod def dbapi(cls): - try: - from pysqlite2 import dbapi2 as sqlite - except ImportError: + if util.py2k: try: - from sqlite3 import dbapi2 as sqlite # try 2.5+ stdlib name. - except ImportError as e: - raise e + from pysqlite2 import dbapi2 as sqlite + except ImportError: + try: + from sqlite3 import dbapi2 as sqlite + except ImportError as e: + raise e + else: + from sqlite3 import dbapi2 as sqlite return sqlite @classmethod diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index f66f06415..79b8622d5 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -65,7 +65,6 @@ class DefaultDialect(interfaces.Dialect): postfetch_lastrowid = True implicit_returning = False - supports_right_nested_joins = True cte_follows_insert = False supports_native_enum = False @@ -201,7 +200,6 @@ class DefaultDialect(interfaces.Dialect): paramstyle=None, dbapi=None, implicit_returning=None, - supports_right_nested_joins=None, case_sensitive=True, supports_native_boolean=None, empty_in_strategy="static", @@ -232,8 +230,6 @@ class DefaultDialect(interfaces.Dialect): self.positional = self.paramstyle in ("qmark", "format", "numeric") self.identifier_preparer = self.preparer(self) self.type_compiler = self.type_compiler(self) - if supports_right_nested_joins is not None: - self.supports_right_nested_joins = supports_right_nested_joins if supports_native_boolean is not None: self.supports_native_boolean = supports_native_boolean self.case_sensitive = case_sensitive diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 1381e734c..320c7b782 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -2049,136 +2049,6 @@ class SQLCompiler(Compiled): def get_statement_hint_text(self, hint_texts): return " ".join(hint_texts) - def _transform_select_for_nested_joins(self, select): - """Rewrite any "a JOIN (b JOIN c)" expression as - "a JOIN (select * from b JOIN c) AS anon", to support - databases that can't parse a parenthesized join correctly - (i.e. sqlite < 3.7.16). - - """ - cloned = {} - column_translate = [{}] - created = set() - - def visit(element, **kw): - if element in column_translate[-1]: - return column_translate[-1][element] - - elif element in cloned: - return cloned[element] - - newelem = cloned[element] = element._clone() - if ( - newelem._is_from_clause - and newelem._is_join - and isinstance(newelem.right, selectable.FromGrouping) - ): - - newelem._reset_exported() - newelem.left = visit(newelem.left, **kw) - - right = visit(newelem.right, **kw) - - selectable_ = selectable.Select( - [right.element], use_labels=True - ).alias() - created.add(selectable_) - created.update(selectable_.c) - - for c in selectable_.c: - c._key_label = c.key - c._label = c.name - - translate_dict = dict( - zip(newelem.right.element.c, selectable_.c) - ) - - # translating from both the old and the new - # because different select() structures will lead us - # to traverse differently - translate_dict[right.element.left] = selectable_ - translate_dict[right.element.right] = selectable_ - translate_dict[newelem.right.element.left] = selectable_ - translate_dict[newelem.right.element.right] = selectable_ - - # propagate translations that we've gained - # from nested visit(newelem.right) outwards - # to the enclosing select here. this happens - # only when we have more than one level of right - # join nesting, i.e. "a JOIN (b JOIN (c JOIN d))" - for k, v in list(column_translate[-1].items()): - if v in translate_dict: - # remarkably, no current ORM tests (May 2013) - # hit this condition, only test_join_rewriting - # does. - column_translate[-1][k] = translate_dict[v] - - column_translate[-1].update(translate_dict) - - newelem.right = selectable_ - - newelem.onclause = visit(newelem.onclause, **kw) - - elif newelem._is_from_container: - # if we hit an Alias, CompoundSelect or ScalarSelect, put a - # marker in the stack. - kw["transform_clue"] = "select_container" - newelem._copy_internals(clone=visit, **kw) - elif newelem._is_returns_rows and newelem._is_select_statement: - barrier_select = ( - kw.get("transform_clue", None) == "select_container" - ) - # if we're still descended from an - # Alias/CompoundSelect/ScalarSelect, we're - # in a FROM clause, so start with a new translate collection - if barrier_select: - column_translate.append({}) - kw["transform_clue"] = "inside_select" - if not newelem._is_select_container: - froms = newelem.froms - newelem._raw_columns = list(newelem.selected_columns) - newelem._from_obj.update(froms) - newelem._reset_memoizations() - newelem._copy_internals(clone=visit, **kw) - if barrier_select: - del column_translate[-1] - else: - newelem._copy_internals(clone=visit, **kw) - - return newelem - - return visit(select) - - def _transform_result_map_for_nested_joins( - self, select, transformed_select - ): - self._result_columns[:] = [ - result_rec - if col is tcol - else ( - result_rec[0], - name, - tuple([col if obj is tcol else obj for obj in result_rec[2]]), - result_rec[3], - ) - for result_rec, (name, col), (tname, tcol) in zip( - self._result_columns, - select._columns_plus_names, - transformed_select._columns_plus_names, - ) - ] - - # TODO: it's not anticipated that we need to correct anon_map - # however if we do, this is what it looks like: - # for (name, col), (tname, tcol) in zip( - # select._columns_plus_names, - # transformed_select._columns_plus_names, - # ): - # if isinstance(name, elements._anonymous_label) and name != tname: - # m1 = re.match(r"^%\((\d+ .+?)\)s$", name) - # m2 = re.match(r"^%\((\d+ .+?)\)s$", tname) - # self.anon_map[m1.group(1)] = self.anon_map[m2.group(1)] - _default_stack_entry = util.immutabledict( [("correlate_froms", frozenset()), ("asfrom_froms", frozenset())] ) @@ -2214,32 +2084,11 @@ class SQLCompiler(Compiled): asfrom=False, fromhints=None, compound_index=0, - nested_join_translation=False, select_wraps_for=None, lateral=False, **kwargs ): - needs_nested_translation = ( - select.use_labels - and not nested_join_translation - and not self.stack - and not self.dialect.supports_right_nested_joins - ) - - if needs_nested_translation: - transformed_select = self._transform_select_for_nested_joins( - select - ) - text = self.visit_select( - transformed_select, - asfrom=asfrom, - fromhints=fromhints, - compound_index=compound_index, - nested_join_translation=True, - **kwargs - ) - toplevel = not self.stack entry = self._default_stack_entry if toplevel else self.stack[-1] @@ -2258,13 +2107,6 @@ class SQLCompiler(Compiled): if not populate_result_map and "add_to_result_map" in kwargs: del kwargs["add_to_result_map"] - if needs_nested_translation: - if populate_result_map: - self._transform_result_map_for_nested_joins( - select, transformed_select - ) - return text - froms = self._setup_select_stack(select, entry, asfrom, lateral) column_clause_args = kwargs.copy() |
