From: Mike Bayer Date: Wed, 29 Nov 2006 22:13:58 +0000 (+0000) Subject: - sending a selectable to an IN no longer creates a "union" out of multiple X-Git-Tag: rel_0_3_2~31 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=13d989b2d0064b00c4b231c9d90bf4a2a6dfd56a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - sending a selectable to an IN no longer creates a "union" out of multiple selects; only one selectable to an IN is allowed now (make a union yourself if union is needed; explicit better than implicit, dont guess, etc.) --- diff --git a/CHANGES b/CHANGES index 979041842a..df487cf267 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ to create a scalar subquery. - added extra check to "stop" cascading on save/update/save-update if an instance is detected to be already in the session. - fix to session.update() to preserve "dirty" status of incoming object +- sending a selectable to an IN no longer creates a "union" out of multiple +selects; only one selectable to an IN is allowed now (make a union yourself +if union is needed; explicit better than implicit, dont guess, etc.) 0.3.1 - Engine/Pool: diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py index 17c0a25f76..acdba2c700 100644 --- a/lib/sqlalchemy/sql.py +++ b/lib/sqlalchemy/sql.py @@ -545,10 +545,12 @@ class _CompareMixin(object): elif _is_literal(other[0]): return self._compare('IN', ClauseList(parens=True, *[self._bind_param(o) for o in other]), negate='NOT IN') else: - # assume *other is a list of selects. - # so put them in a UNION. if theres only one, you just get one SELECT - # statement out of it. - return self._compare('IN', union(parens=True, *other), negate='NOT IN') + # assume *other is a single select. + # originally, this assumed possibly multiple selects and created a UNION, + # but we are now forcing explictness if a UNION is desired. + if len(other) > 1: + raise exceptions.InvalidRequestException("in() function accepts only multiple literal values, or a single selectable as an argument") + return self._compare('IN', other[0], negate='NOT IN') def startswith(self, other): return self._compare('LIKE', other + "%") def endswith(self, other): @@ -1366,6 +1368,7 @@ class CompoundSelect(_SelectBaseMixin, FromClause): self.selects = selects + # some DBs do not like ORDER BY in the inner queries of a UNION, etc. for s in selects: s.group_by(None) s.order_by(None) diff --git a/test/sql/select.py b/test/sql/select.py index 1c8927e6b1..c422c63fbe 100644 --- a/test/sql/select.py +++ b/test/sql/select.py @@ -572,6 +572,18 @@ FROM mytable, myothertable WHERE mytable.myid = myothertable.otherid AND mytable self.runtest(select([table1], ~table1.c.myid.in_(select([table2.c.otherid]))), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid NOT IN (SELECT myothertable.otherid AS otherid FROM myothertable)") + + # test that putting a select in an IN clause does not blow away its ORDER BY clause + self.runtest( + select([table1, table2], + table2.c.otherid.in_( + select([table2.c.otherid], order_by=[table2.c.othername], limit=10, correlate=False) + ), + from_obj=[table1.join(table2, table1.c.myid==table2.c.otherid)], order_by=[table1.c.myid] + ), + "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid WHERE myothertable.otherid IN (SELECT myothertable.otherid AS otherid FROM myothertable ORDER BY myothertable.othername LIMIT 10) ORDER BY mytable.myid" + ) + def testlateargs(self): """tests that a SELECT clause will have extra "WHERE" clauses added to it at compile time if extra arguments