summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2019-06-13 18:14:12 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2019-06-13 18:14:12 +0000
commit750c2d99c6ffa24161852973f045b5a1449b4f6c (patch)
tree9a96b8ac66976a94f9133a339bee6baba8e22d35
parente50da587781d9a1fc48c7505e5f6a661155a3b54 (diff)
parent3002c560ee0e23e045ff67617838220e736d31fc (diff)
downloadsqlalchemy-750c2d99c6ffa24161852973f045b5a1449b4f6c.tar.gz
Merge "Reverse Alias nesting concept"
-rw-r--r--lib/sqlalchemy/dialects/firebird/base.py4
-rw-r--r--lib/sqlalchemy/dialects/mssql/base.py2
-rw-r--r--lib/sqlalchemy/sql/coercions.py12
-rw-r--r--lib/sqlalchemy/sql/compiler.py6
-rw-r--r--lib/sqlalchemy/sql/selectable.py51
-rw-r--r--test/sql/test_selectable.py72
6 files changed, 114 insertions, 33 deletions
diff --git a/lib/sqlalchemy/dialects/firebird/base.py b/lib/sqlalchemy/dialects/firebird/base.py
index 7ae198f6c..e67bb2d38 100644
--- a/lib/sqlalchemy/dialects/firebird/base.py
+++ b/lib/sqlalchemy/dialects/firebird/base.py
@@ -469,12 +469,12 @@ class FBCompiler(sql.compiler.SQLCompiler):
)
return (
- self.process(alias.original, asfrom=asfrom, **kwargs)
+ self.process(alias.element, asfrom=asfrom, **kwargs)
+ " "
+ self.preparer.format_alias(alias, alias_name)
)
else:
- return self.process(alias.original, **kwargs)
+ return self.process(alias.element, **kwargs)
def visit_substring_func(self, func, **kw):
s = self.process(func.clauses.clauses[0])
diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py
index 00a110aa2..8c3d5b3fa 100644
--- a/lib/sqlalchemy/dialects/mssql/base.py
+++ b/lib/sqlalchemy/dialects/mssql/base.py
@@ -1659,7 +1659,7 @@ class MSSQLCompiler(compiler.SQLCompiler):
@_with_legacy_schema_aliasing
def visit_alias(self, alias, **kw):
# translate for schema-qualified table aliases
- kw["mssql_aliased"] = alias.original
+ kw["mssql_aliased"] = alias.element
return super(MSSQLCompiler, self).visit_alias(alias, **kw)
@_with_legacy_schema_aliasing
diff --git a/lib/sqlalchemy/sql/coercions.py b/lib/sqlalchemy/sql/coercions.py
index d4551eb60..ccd506a73 100644
--- a/lib/sqlalchemy/sql/coercions.py
+++ b/lib/sqlalchemy/sql/coercions.py
@@ -176,10 +176,10 @@ class _ColumnCoercions(object):
elif (
resolved._is_from_clause
and isinstance(resolved, selectable.Alias)
- and resolved.original._is_select_statement
+ and resolved.element._is_select_statement
):
self._warn_for_scalar_subquery_coercion()
- return resolved.original.scalar_subquery()
+ return resolved.element.scalar_subquery()
else:
self._raise_for_expected(original_element, argname)
@@ -282,9 +282,9 @@ class InElementImpl(RoleImpl, roles.InElementRole):
if resolved._is_from_clause:
if (
isinstance(resolved, selectable.Alias)
- and resolved.original._is_select_statement
+ and resolved.element._is_select_statement
):
- return resolved.original
+ return resolved.element
else:
return resolved.select()
else:
@@ -579,9 +579,9 @@ class DMLSelectImpl(_NoTextCoercion, RoleImpl, roles.DMLSelectRole):
if resolved._is_from_clause:
if (
isinstance(resolved, selectable.Alias)
- and resolved.original._is_select_statement
+ and resolved.element._is_select_statement
):
- return resolved.original
+ return resolved.element
else:
return resolved.select()
else:
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 8080d2cc6..6fcf1a524 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -1680,7 +1680,7 @@ class SQLCompiler(Compiled):
if self.positional:
kwargs["positional_names"] = self.cte_positional[cte] = []
- text += " AS \n" + cte.original._compiler_dispatch(
+ text += " AS \n" + cte.element._compiler_dispatch(
self, asfrom=True, **kwargs
)
@@ -1722,7 +1722,7 @@ class SQLCompiler(Compiled):
if ashint:
return self.preparer.format_alias(alias, alias_name)
elif asfrom:
- ret = alias.original._compiler_dispatch(
+ ret = alias.element._compiler_dispatch(
self, asfrom=True, **kwargs
) + self.get_render_as_alias_suffix(
self.preparer.format_alias(alias, alias_name)
@@ -1735,7 +1735,7 @@ class SQLCompiler(Compiled):
return ret
else:
- return alias.original._compiler_dispatch(self, **kwargs)
+ return alias.element._compiler_dispatch(self, **kwargs)
def visit_lateral(self, lateral, **kw):
kw["lateral"] = True
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index b0d6002b7..014c782d0 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -1261,26 +1261,30 @@ class Alias(roles.AnonymizedFromClauseRole, FromClause):
)
def _init(self, selectable, name=None):
- baseselectable = selectable
- while isinstance(baseselectable, Alias):
- baseselectable = baseselectable.element
- self.original = baseselectable
- self.supports_execution = baseselectable.supports_execution
+ self.wrapped = selectable
+ if isinstance(selectable, Alias):
+ selectable = selectable.element
+ assert not isinstance(selectable, Alias)
+
+ self.supports_execution = selectable.supports_execution
if self.supports_execution:
- self._execution_options = baseselectable._execution_options
+ self._execution_options = selectable._execution_options
self.element = selectable
self._orig_name = name
if name is None:
- if self.original.named_with_column:
- name = getattr(self.original, "name", None)
+ if (
+ isinstance(selectable, FromClause)
+ and selectable.named_with_column
+ ):
+ name = getattr(selectable, "name", None)
name = _anonymous_label("%%(%d %s)s" % (id(self), name or "anon"))
self.name = name
def self_group(self, against=None):
if (
isinstance(against, CompoundSelect)
- and isinstance(self.original, Select)
- and self.original._needs_parens_for_grouping()
+ and isinstance(self.element, Select)
+ and self.element._needs_parens_for_grouping()
):
return FromGrouping(self)
@@ -1293,17 +1297,22 @@ class Alias(roles.AnonymizedFromClauseRole, FromClause):
else:
return self.name.encode("ascii", "backslashreplace")
+ @property
+ def original(self):
+ """legacy for dialects that are referring to Alias.original"""
+ return self.element
+
def is_derived_from(self, fromclause):
if fromclause in self._cloned_set:
return True
return self.element.is_derived_from(fromclause)
def _populate_column_collection(self):
- for col in self.element.columns._all_columns:
+ for col in self.wrapped.columns._all_columns:
col._make_proxy(self)
def _refresh_for_new_column(self, column):
- col = self.element._refresh_for_new_column(column)
+ col = self.wrapped._refresh_for_new_column(column)
if col is not None:
if not self._cols_populated:
return None
@@ -1319,17 +1328,17 @@ class Alias(roles.AnonymizedFromClauseRole, FromClause):
if isinstance(self.element, TableClause):
return
self._reset_exported()
- self.element = clone(self.element, **kw)
- baseselectable = self.element
- while isinstance(baseselectable, Alias):
- baseselectable = baseselectable.element
- self.original = baseselectable
+ self.wrapped = clone(self.wrapped, **kw)
+ if isinstance(self.wrapped, Alias):
+ self.element = self.wrapped.element
+ else:
+ self.element = self.wrapped
def get_children(self, column_collections=True, **kw):
if column_collections:
for c in self.c:
yield c
- yield self.element
+ yield self.wrapped
def _cache_key(self, **kw):
return (self.__class__, self.element._cache_key(**kw), self._orig_name)
@@ -1522,7 +1531,7 @@ class CTE(Generative, HasSuffixes, Alias):
def alias(self, name=None, flat=False):
return CTE._construct(
- self.original,
+ self.element,
name=name,
recursive=self.recursive,
_cte_alias=self,
@@ -1531,7 +1540,7 @@ class CTE(Generative, HasSuffixes, Alias):
def union(self, other):
return CTE._construct(
- self.original.union(other),
+ self.element.union(other),
name=self.name,
recursive=self.recursive,
_restates=self._restates.union([self]),
@@ -1540,7 +1549,7 @@ class CTE(Generative, HasSuffixes, Alias):
def union_all(self, other):
return CTE._construct(
- self.original.union_all(other),
+ self.element.union_all(other),
name=self.name,
recursive=self.recursive,
_restates=self._restates.union([self]),
diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py
index 186fb3d9e..f525703f1 100644
--- a/test/sql/test_selectable.py
+++ b/test/sql/test_selectable.py
@@ -931,6 +931,22 @@ class RefreshForNewColTest(fixtures.TestBase):
s._refresh_for_new_column(q)
assert q in s.c.b_x.proxy_set
+ def test_alias_alias_samename_init(self):
+ a = table("a", column("x"))
+ b = table("b", column("y"))
+ s1 = select([a, b]).apply_labels().alias()
+ s2 = s1.alias()
+
+ s1.c
+ s2.c
+
+ q = column("x")
+ b.append_column(q)
+
+ s2._refresh_for_new_column(q)
+
+ is_(s1.corresponding_column(s2.c.b_x), s1.c.b_x)
+
def test_aliased_select_samename_uninit(self):
a = table("a", column("x"))
b = table("b", column("y"))
@@ -2584,3 +2600,59 @@ class ForUpdateTest(fixtures.TestBase, AssertsCompiledSQL):
"SELECT t_1.c FROM t AS t_1 FOR SHARE OF t_1",
dialect="postgresql",
)
+
+
+class AliasTest(fixtures.TestBase, AssertsCompiledSQL):
+ __dialect__ = "default"
+
+ def test_legacy_original_accessor(self):
+ t = table("t", column("c"))
+ a1 = t.alias()
+ a2 = a1.alias()
+ a3 = a2.alias()
+
+ is_(a1.original, t)
+ is_(a2.original, t)
+ is_(a3.original, t)
+
+ def test_wrapped(self):
+ t = table("t", column("c"))
+ a1 = t.alias()
+ a2 = a1.alias()
+ a3 = a2.alias()
+
+ is_(a1.element, t)
+ is_(a2.element, t)
+ is_(a3.element, t)
+
+ is_(a3.wrapped, a2)
+ is_(a2.wrapped, a1)
+ is_(a1.wrapped, t)
+
+ def test_get_children_preserves_wrapped(self):
+ t = table("t", column("c"))
+ stmt = select([t])
+ a1 = stmt.alias()
+ a2 = a1.alias()
+ eq_(set(a2.get_children(column_collections=False)), {a1})
+
+ def test_wrapped_correspondence(self):
+ t = table("t", column("c"))
+ stmt = select([t])
+ a1 = stmt.alias()
+ a2 = a1.alias()
+
+ is_(a1.corresponding_column(a2.c.c), a1.c.c)
+
+ def test_copy_internals_preserves_wrapped(self):
+ t = table("t", column("c"))
+ stmt = select([t])
+ a1 = stmt.alias()
+ a2 = a1.alias()
+
+ is_(a2.element, a2.wrapped.element)
+
+ a3 = a2._clone()
+ a3._copy_internals()
+ is_(a1.corresponding_column(a3.c.c), a1.c.c)
+ is_(a3.element, a3.wrapped.element)