]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- version bump
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 16 Sep 2008 18:17:34 +0000 (18:17 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 16 Sep 2008 18:17:34 +0000 (18:17 +0000)
- turned properties in sql/expressions.py to @property
- column.in_(someselect) can now be used as
a columns-clause expression without the subquery
bleeding into the FROM clause [ticket:1074]

CHANGES
VERSION
lib/sqlalchemy/sql/expression.py
test/sql/select.py

diff --git a/CHANGES b/CHANGES
index ee667c3cf994ca63ca756c77d10122e8cc4ada2e..41e38e72bca3008e6a40106b2474e3e4669b0800 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,14 @@
 CHANGES
 =======
 
+0.5.0rc2
+========
+
+- sql
+    - column.in_(someselect) can now be used as 
+      a columns-clause expression without the subquery
+      bleeding into the FROM clause [ticket:1074]
+    
 0.5.0rc1
 ========
 
diff --git a/VERSION b/VERSION
index 3dc6a34dcec376a2bafe8527aa01681a01330d9d..9bd452b25b39f818ed426d1d8cca75bcdfdaaa2d 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.5.0rc1
+0.5.0rc2
index dfc6bd8fae3ab39364a847ff7e2bc4192c309951..81c9d3936330f24b4becdfc9ee3b8c055643ff26 100644 (file)
@@ -979,12 +979,12 @@ class ClauseElement(object):
 
         return c
 
+    @property
     def _cloned_set(self):
         f = self
         while f is not None:
             yield f
             f = getattr(f, '_is_clone_of', None)
-    _cloned_set = property(_cloned_set)
 
     def _get_from_objects(self, **modifiers):
         """Return objects represented in this ``ClauseElement`` that
@@ -1075,6 +1075,7 @@ class ClauseElement(object):
     def self_group(self, against=None):
         return self
 
+    @property
     def bind(self):
         """Returns the Engine or Connection to which this ClauseElement is bound, or None if none found."""
 
@@ -1091,7 +1092,6 @@ class ClauseElement(object):
                 return engine
         else:
             return None
-    bind = property(bind)
 
     def execute(self, *multiparams, **params):
         """Compile and execute this ``ClauseElement``."""
@@ -1360,7 +1360,16 @@ class _CompareMixin(ColumnOperators):
         return self._in_impl(operators.in_op, operators.notin_op, other)
 
     def _in_impl(self, op, negate_op, seq_or_selectable):
-        if isinstance(seq_or_selectable, Selectable):
+        if isinstance(seq_or_selectable, _ScalarSelect):
+            return self.__compare( op, seq_or_selectable, negate=negate_op)
+            
+        elif isinstance(seq_or_selectable, _SelectBaseMixin):
+            # TODO: if we ever want to support (x, y, z) IN (select x, y, z from table),
+            # we would need a multi-column version of as_scalar() to produce a multi-
+            # column selectable that does not export itself as a FROM clause
+            return self.__compare( op, seq_or_selectable.as_scalar(), negate=negate_op)
+            
+        elif isinstance(seq_or_selectable, Selectable):
             return self.__compare( op, seq_or_selectable, negate=negate_op)
 
         # Handle non selectable arguments as sequences
@@ -1501,13 +1510,14 @@ class ColumnElement(ClauseElement, _CompareMixin):
     foreign_keys = []
     quote = None
     
+    @property
     def base_columns(self):
         if not hasattr(self, '_base_columns'):
             self._base_columns = set(c for c in self.proxy_set
                                      if not hasattr(c, 'proxies'))
         return self._base_columns
-    base_columns = property(base_columns)
 
+    @property
     def proxy_set(self):
         if not hasattr(self, '_proxy_set'):
             s = set([self])
@@ -1516,7 +1526,6 @@ class ColumnElement(ClauseElement, _CompareMixin):
                     s.update(c.proxy_set)
             self._proxy_set = s
         return self._proxy_set
-    proxy_set = property(proxy_set)
 
     def shares_lineage(self, othercolumn):
         """Return True if the given ``ColumnElement`` has a common ancestor to this ``ColumnElement``.
@@ -1540,6 +1549,7 @@ class ColumnElement(ClauseElement, _CompareMixin):
         selectable.columns[name] = co
         return co
 
+    @property
     def anon_label(self):
         """provides a constant 'anonymous label' for this ColumnElement.
 
@@ -1556,7 +1566,6 @@ class ColumnElement(ClauseElement, _CompareMixin):
         if not hasattr(self, '_ColumnElement__anon_label'):
             self.__anon_label = "{ANON %d %s}" % (id(self), getattr(self, 'name', 'anon'))
         return self.__anon_label
-    anon_label = property(anon_label)
 
 class ColumnCollection(util.OrderedProperties):
     """An ordered dictionary that stores a list of ColumnElement
@@ -1767,13 +1776,13 @@ class FromClause(Selectable):
                 col, intersect = c, i
         return col
 
+    @property
     def description(self):
         """a brief description of this FromClause.
 
         Used primarily for error message formatting.
         """
         return getattr(self, 'name', self.__class__.__name__ + " object")
-    description = property(description)
 
     def _reset_exported(self):
         # delete all the "generated" collections of columns for a
@@ -1959,12 +1968,12 @@ class _TextClause(ClauseElement):
             for b in bindparams:
                 self.bindparams[b.key] = b
 
+    @property
     def type(self):
         if self.typemap is not None and len(self.typemap) == 1:
             return list(self.typemap)[0]
         else:
             return None
-    type = property(type)
 
     def _copy_internals(self, clone=_clone):
         self.bindparams = dict((b.key, clone(b))
@@ -2082,19 +2091,19 @@ class _CalculatedClause(ColumnElement):
         else:
             self.clause_expr = clauses
 
+    @property
     def key(self):
         return self.name or '_calc_'
-    key = property(key)
 
     def _copy_internals(self, clone=_clone):
         self.clause_expr = clone(self.clause_expr)
 
+    @property
     def clauses(self):
         if isinstance(self.clause_expr, _Grouping):
             return self.clause_expr.element
         else:
             return self.clause_expr
-    clauses = property(clauses)
 
     def get_children(self, **kwargs):
         return self.clause_expr,
@@ -2132,13 +2141,13 @@ class _Function(_CalculatedClause, FromClause):
         self.clause_expr = ClauseList(operator=operators.comma_op, group_contents=True, *args).self_group()
         self.type = sqltypes.to_instance(kwargs.get('type_', None))
 
+    @property
     def key(self):
         return self.name
-    key = property(key)
 
+    @property
     def columns(self):
         return [self]
-    columns = property(columns)
 
     def _copy_internals(self, clone=_clone):
         _CalculatedClause._copy_internals(self, clone=clone)
@@ -2324,9 +2333,9 @@ class Join(FromClause):
         self.isouter = isouter
         self.__folded_equivalents = None
 
+    @property
     def description(self):
         return "Join object on %s(%d) and %s(%d)" % (self.left.description, id(self.left), self.right.description, id(self.right))
-    description = property(description)
 
     def is_derived_from(self, fromclause):
         return fromclause is self or self.left.is_derived_from(fromclause) or self.right.is_derived_from(fromclause)
@@ -2392,9 +2401,9 @@ class Join(FromClause):
 
         return select(collist, whereclause, from_obj=[self], **kwargs)
 
+    @property
     def bind(self):
         return self.left.bind or self.right.bind
-    bind = property(bind)
 
     def alias(self, name=None):
         """Create a ``Select`` out of this ``Join`` clause and return an ``Alias`` of it.
@@ -2404,9 +2413,9 @@ class Join(FromClause):
 
         return self.select(use_labels=True, correlate=False).alias(name)
 
+    @property
     def _hide_froms(self):
         return itertools.chain(*[_from_objects(x.left, x.right) for x in self._cloned_set])
-    _hide_froms = property(_hide_froms)
 
     def _get_from_objects(self, **modifiers):
         return [self] + self.onclause._get_from_objects(**modifiers) + self.left._get_from_objects(**modifiers) + self.right._get_from_objects(**modifiers)
@@ -2438,9 +2447,9 @@ class Alias(FromClause):
             alias = '{ANON %d %s}' % (id(self), alias or 'anon')
         self.name = alias
 
+    @property
     def description(self):
         return self.name.encode('ascii', 'backslashreplace')
-    description = property(description)
 
     def is_derived_from(self, fromclause):
         if fromclause in set(self._cloned_set):
@@ -2471,9 +2480,9 @@ class Alias(FromClause):
     def _get_from_objects(self, **modifiers):
         return [self]
 
+    @property
     def bind(self):
         return self.element.bind
-    bind = property(bind)
 
 class _Grouping(ColumnElement):
     """Represent a grouping within a column expression"""
@@ -2483,16 +2492,16 @@ class _Grouping(ColumnElement):
         self.element = element
         self.type = getattr(element, 'type', None)
 
+    @property
     def key(self):
         return self.element.key
-    key = property(key)
 
+    @property
     def _label(self):
         try:
             return self.element._label
         except AttributeError:
             return self.anon_label
-    _label = property(_label)
 
     def _copy_internals(self, clone=_clone):
         self.element = clone(self.element)
@@ -2520,13 +2529,14 @@ class _FromGrouping(FromClause):
     def __init__(self, element):
         self.element = element
 
+    @property
     def columns(self):
         return self.element.columns
-    columns = c = property(columns)
+    c = columns
 
+    @property
     def _hide_froms(self):
         return self.element._hide_froms
-    _hide_froms = property(_hide_froms)
 
     def get_children(self, **kwargs):
         return self.element,
@@ -2559,13 +2569,13 @@ class _Label(ColumnElement):
         self.type = sqltypes.to_instance(type_ or getattr(element, 'type', None))
         self.quote = element.quote
 
+    @property
     def key(self):
         return self.name
-    key = property(key)
 
+    @property
     def _label(self):
         return self.name
-    _label = property(_label)
 
     def _proxy_attr(name):
         get = attrgetter(name)
@@ -2632,10 +2642,11 @@ class _ColumnClause(_Immutable, ColumnElement):
         self.__label = None
         self.is_literal = is_literal
 
+    @property
     def description(self):
         return self.name.encode('ascii', 'backslashreplace')
-    description = property(description)
 
+    @property
     def _label(self):
         if self.is_literal:
             return None
@@ -2656,7 +2667,6 @@ class _ColumnClause(_Immutable, ColumnElement):
             else:
                 self.__label = self.name
         return self.__label
-    _label = property(_label)
 
     def label(self, name):
         if name is None:
@@ -2709,9 +2719,9 @@ class TableClause(_Immutable, FromClause):
     def _export_columns(self):
         raise NotImplementedError()
 
+    @property
     def description(self):
         return self.name.encode('ascii', 'backslashreplace')
-    description = property(description)
 
     def append_column(self, c):
         self._columns[c.name] = c
@@ -2876,14 +2886,15 @@ class _ScalarSelect(_Grouping):
 
     def __init__(self, element):
         self.element = element
-        cols = list(element.inner_columns)
+        cols = list(element.c)
         if len(cols) != 1:
             raise exc.InvalidRequestError("Scalar select can only be created from a Select object that has exactly one column expression.")
         self.type = cols[0].type
 
+    @property
     def columns(self):
         raise exc.InvalidRequestError("Scalar Select expression has no columns; use this object directly within a column-level expression.")
-    columns = c = property(columns)
+    c = columns
 
     def self_group(self, **kwargs):
         return self
@@ -2919,7 +2930,7 @@ class CompoundSelect(_SelectBaseMixin, FromClause):
                 self.selects.append(s)
 
         _SelectBaseMixin.__init__(self, **kwargs)
-        
+
     def self_group(self, against=None):
         return _FromGrouping(self)
 
@@ -3048,11 +3059,15 @@ class Select(_SelectBaseMixin, FromClause):
                     
         return froms
 
-    froms = property(_get_display_froms, doc="""Return a list of all FromClause elements which will be applied to the FROM clause of the resulting statement.""")
+    @property
+    def froms(self):
+        """Return a list of all FromClause elements which will be applied to the FROM clause of the resulting statement."""
+        
+        return self._get_display_froms()
 
+    @property
     def type(self):
         raise exc.InvalidRequestError("Select objects don't have a type.  Call as_scalar() on this Select object to return a 'scalar' version of this Select.")
-    type = property(type)
 
     def locate_all_froms(self):
         """return a Set of all FromClause elements referenced by this Select.
@@ -3066,6 +3081,7 @@ class Select(_SelectBaseMixin, FromClause):
 
         return self._all_froms
 
+    @property
     def inner_columns(self):
         """an iteratorof all ColumnElement expressions which would
         be rendered into the columns clause of the resulting SELECT statement.
@@ -3077,7 +3093,6 @@ class Select(_SelectBaseMixin, FromClause):
                     yield co
             else:
                 yield c
-    inner_columns = property(inner_columns)
 
     def is_derived_from(self, fromclause):
         if self in set(fromclause._cloned_set):
index facfb52870e973e536e26df1076759309c66ac53..3b6964e7db2bac62811a64d854f8f3c54ea510fa 100644 (file)
@@ -1166,13 +1166,13 @@ UNION SELECT mytable.myid FROM mytable"
 
         self.assert_compile(select([table1], table1.c.myid.in_(
             union(
-                  select([table1], table1.c.myid == 5),
-                  select([table1], table1.c.myid == 12),
+                  select([table1.c.myid], table1.c.myid == 5),
+                  select([table1.c.myid], table1.c.myid == 12),
             )
         )), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable \
 WHERE mytable.myid IN (\
-SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 \
-UNION SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_2)")
+SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_1 \
+UNION SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_2)")
 
         # test that putting a select in an IN clause does not blow away its ORDER BY clause
         self.assert_compile(
@@ -1191,6 +1191,15 @@ UNION SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE
         self.assert_compile(select([table1], table1.c.myid.in_([])),
         "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE (CASE WHEN (mytable.myid IS NULL) THEN NULL ELSE 0 END = 1)")
 
+        self.assert_compile(
+            select([table1.c.myid.in_(select([table2.c.otherid]))]),
+            "SELECT mytable.myid IN (SELECT myothertable.otherid FROM myothertable) AS anon_1 FROM mytable"
+        )
+        self.assert_compile(
+            select([table1.c.myid.in_(select([table2.c.otherid]).as_scalar())]),
+            "SELECT mytable.myid IN (SELECT myothertable.otherid FROM myothertable) AS anon_1 FROM mytable"
+        )
+
     def test_cast(self):
         tbl = table('casttest',
                     column('id', Integer),