An exception is now raised if the version id is programmatic and
was set to NULL for an UPDATE. Pull request courtesy Diana Clarke.
+ .. change:: 3999
+ :tags: bug, sql
+ :tickets: 3999
+
+ The operator precedence for all comparison operators such as LIKE, IS,
+ IN, MATCH, equals, greater than, less than, etc. has all been merged
+ into one level, so that expressions which make use of these against
+ each other will produce parentheses between them. This suits the
+ stated operator precedence of databases like Oracle, MySQL and others
+ which place all of these operators as equal precedence, as well as
+ Postgresql as of 9.5 which has also flattened its operator precendence.
+
+ .. seealso::
+
+ :ref:`change_3999`
+
+
.. change:: 3796
:tags: bug, orm
:tickets: 3796
:ticket:`3953`
+.. _change_3999:
+
+Flattened operator precedence for comparison operators
+-------------------------------------------------------
+
+The operator precedence for operators like IN, LIKE, equals, IS, MATCH, and
+other comparison operators has been flattened into one level. This will
+have the effect of more parenthesization being generated when comparison
+operators are combined together, such as::
+
+ (column('q') == null()) != (column('y') == null())
+
+Will now generate ``(q IS NULL) != (y IS NULL)`` rather than
+``q IS NULL != y IS NULL``.
+
+
+:ticket:`3999`
+
.. _change_1546:
Support for SQL Comments on Table, Column, includes DDL, reflection
... addresses.c.email_address.like(users.c.name + '%')
... )
... )
- users JOIN addresses ON addresses.email_address LIKE (users.name || :name_1)
+ users JOIN addresses ON addresses.email_address LIKE users.name || :name_1
When we create a :func:`.select` construct, SQLAlchemy looks around at the
tables we've mentioned and then places them in the FROM clause of the
... )
{sql}>>> conn.execute(s).fetchall()
SELECT users.fullname
- FROM users JOIN addresses ON addresses.email_address LIKE (users.name || ?)
+ FROM users JOIN addresses ON addresses.email_address LIKE users.name || ?
('%',)
{stop}[(u'Jack Jones',), (u'Jack Jones',), (u'Wendy Williams',)]
{sql}>>> conn.execute(s, username='wendy').fetchall()
SELECT users.id, users.name, users.fullname
FROM users
- WHERE users.name LIKE (? || '%')
+ WHERE users.name LIKE ? || '%'
('wendy',)
{stop}[(2, u'wendy', u'Wendy Williams')]
SELECT users.id, users.name, users.fullname, addresses.id,
addresses.user_id, addresses.email_address
FROM users LEFT OUTER JOIN addresses ON users.id = addresses.user_id
- WHERE users.name LIKE (? || '%') OR addresses.email_address LIKE (? || '@%')
+ WHERE users.name LIKE ? || '%' OR addresses.email_address LIKE ? || '@%'
ORDER BY addresses.id
('jack', 'jack')
{stop}[(1, u'jack', u'Jack Jones', 1, 1, u'jack@yahoo.com'), (1, u'jack', u'Jack Jones', 2, 1, u'jack@msn.com')]
sub: 7,
concat_op: 6,
- match_op: 6,
- notmatch_op: 6,
-
- ilike_op: 6,
- notilike_op: 6,
- like_op: 6,
- notlike_op: 6,
- in_op: 6,
- notin_op: 6,
-
- is_: 6,
- isnot: 6,
+
+ match_op: 5,
+ notmatch_op: 5,
+
+ ilike_op: 5,
+ notilike_op: 5,
+ like_op: 5,
+ notlike_op: 5,
+ in_op: 5,
+ notin_op: 5,
+
+ is_: 5,
+ isnot: 5,
eq: 5,
ne: 5,
Entity = self.classes.Entity
self.assert_compile(
Entity.descendants.property.strategy._lazywhere,
- "entity.path LIKE (:param_1 || :path_1)"
+ "entity.path LIKE :param_1 || :path_1"
)
self.assert_compile(
Entity.descendants.property.strategy._rev_lazywhere,
- ":param_1 LIKE (entity.path || :path_1)"
+ ":param_1 LIKE entity.path || :path_1"
)
def test_ancestors_lazyload_clause(self):
# :param_1 LIKE (:param_1 || :path_1)
self.assert_compile(
Entity.anscestors.property.strategy._lazywhere,
- ":param_1 LIKE (entity.path || :path_1)"
+ ":param_1 LIKE entity.path || :path_1"
)
self.assert_compile(
Entity.anscestors.property.strategy._rev_lazywhere,
- "entity.path LIKE (:param_1 || :path_1)"
+ "entity.path LIKE :param_1 || :path_1"
)
def test_descendants_lazyload(self):
self.assert_compile(
sess.query(Entity).join(Entity.descendants, aliased=True),
"SELECT entity.path AS entity_path FROM entity JOIN entity AS "
- "entity_1 ON entity_1.path LIKE (entity.path || :path_1)"
+ "entity_1 ON entity_1.path LIKE entity.path || :path_1"
)
self.assert_compile(op2, "mytable.myid hoho :myid_1 lala :param_1")
self.assert_compile(op3, "(mytable.myid hoho :myid_1) lala :param_1")
+ def test_is_eq_precedence_flat(self):
+ self.assert_compile(
+ (self.table1.c.name == null()) !=
+ (self.table1.c.description == null()),
+ "(mytable.name IS NULL) != (mytable.description IS NULL)",
+ )
+
class OperatorAssociativityTest(fixtures.TestBase, testing.AssertsCompiledSQL):
__dialect__ = 'default'