我正在从事的项目是在 RDS 上使用 MySQL(特别是 mysql2 gem)。
当我在 where
语句中使用包含范围的条件散列时,我的查询中添加了一些奇怪的内容。
User.where(id: [1..5])
和
User.where(id: [1...5])
分别产生如下查询:
SELECT `users`.* FROM `users` WHERE ((`users`.`id` BETWEEN 1 AND 5 OR 1=0))
SELECT `users`.* FROM `users` WHERE ((`users`.`id` >= 1 AND `users`.`id` < 5 OR 1=0))
查询工作得很好,因为 OR FALSE
实际上是一个空操作。我只是想知道为什么 Rails 或 ARel 将这段代码添加到查询中。
编辑
看起来可以解释这是line 26 in ActiveRecord::PredicateBuilder
.仍然不知道散列怎么可能是 empty?
,但也许其他人知道。
编辑 2
这很有趣。我正在研究 Filip 的评论,看看他为什么要发表评论,因为它看起来只是一种澄清,但他是正确的 1..5 != [1..5]
。前者是从 1 到 5 的包含范围,而后者是一个数组,其第一个元素是前者。我尝试将它们放入 ARel where
调用中以查看生成的 SQL,但 OR 1=0
不存在!
User.where(id: 1..5) #=> SELECT "users".* FROM "users" WHERE ("users"."id" BETWEEN 1 AND 5)
User.where(id: 1...5) #=> SELECT "users".* FROM "users" WHERE ("users"."id" >= 1 AND "users"."id" < 5)
虽然我仍然不知道为什么 ARel 添加了OR 1=0
,但它始终是错误的并且看似不必要。这可能是由于 Array
和 Range
的处理方式不同。
最佳答案
基于您发现的事实,[1..5]
不是指定范围的正确方法...我发现了为什么 [1. .5]
的行为与它一样。为了到达那里,我首先发现散列条件中的空数组会产生 1=0
SQL 条件:
User.where(id: []).to_sql
# => "SELECT \"users\".* FROM \"users\" WHERE 1=0"
而且,如果您查看 ActiveRecord::PredicateBuilder::ArrayHandler code ,您会看到数组值总是被划分为范围和其他值。
ranges, values = values.partition { |v| v.is_a?(Range) }
这解释了为什么您在使用非范围值时看不到 1=0
。也就是说,从数组中获取 1=0
而不包含范围的唯一方法是提供一个空数组,这会产生 1=0
条件,如上所示.当数组中的所有数组都是一个范围时,您将获得范围条件(ranges
),并分别执行一个空数组条件(values
)。我的猜测是,这没有充分的理由......只是让它变得比避免它更容易(因为结果集是等效的)。如果分区代码更聪明一点,那么它就不必添加额外的空 values
数组,并且可以跳过 1=0
条件。
至于 1=0
首先来自哪里......我认为来自数据库适配器,但我找不到确切的位置。但是,我将其称为未能找到记录的尝试。换句话说,WHERE 1=0
永远不会返回任何用户,这比 WHERE id=null
之类的替代 SQL 更有意义,后者会找到任何 id 的用户为空(意识到这不是真正正确的 SQL 语法)。这就是我在尝试查找 id 在空集中的所有用户时所期望的(即,我们不要求 nil id 或 null id 或其他任何东西)。因此,在我看来,将关于 1=0
的确切来源的部分保留为黑匣子是可以的。至少我们现在可以推断为什么数组内部的范围会导致它出现!
更新
我还发现,即使直接使用ARel,还是可以得到1=0
:
User.arel_table[:id].in([]).to_sql
# => "1=0"
关于mysql - 为什么 Rails 使用带范围的 where 子句哈希语法将 `OR 1=0` 添加到查询中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21886242/