From: Mike Bayer Date: Tue, 16 Sep 2008 18:17:34 +0000 (+0000) Subject: - version bump X-Git-Tag: rel_0_5rc2~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d47a469732f0695c612615819c3273afbb28ed76;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - version bump - 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] --- diff --git a/CHANGES b/CHANGES index ee667c3cf9..41e38e72bc 100644 --- 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 3dc6a34dce..9bd452b25b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.0rc1 +0.5.0rc2 diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index dfc6bd8fae..81c9d39363 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -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): diff --git a/test/sql/select.py b/test/sql/select.py index facfb52870..3b6964e7db 100644 --- a/test/sql/select.py +++ b/test/sql/select.py @@ -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),