【问题标题】:Why is Rails is adding `OR 1=0` to queries using the where clause hash syntax with a range?为什么 Rails 使用带范围的 where 子句哈希语法向查询添加“OR 1=0”?
【发布时间】:2014-03-20 02:57:22
【问题描述】:

我正在进行的项目是在 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 将这个 sn-p 添加到查询中。

编辑

看起来可以解释的那一行是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,这将始终是错误的,并且似乎没有必要。这可能是因为Arrays 和Ranges 的处理方式不同。

【问题讨论】:

  • [1..5] 不是范围。 [1..5].class #&gt; Arraya = [0,1,2,3,4,5,6];a[1..4] #&gt; [1,2,3,4]@aaron

标签: mysql ruby-on-rails between


【解决方案1】:

严格来说这是一个猜测,因为我在自己的项目中做了类似的事情(虽然我使用了AND 1)。

无论出于何种原因,在生成查询时,始终使用包含无操作的 WHERE 子句比有条件地生成 WHERE 子句更容易。也就是说,如果您不包含任何 where 部分,它将最终生成仍然有效的内容。

另一方面,我不确定它为什么采用这种形式:当我这样做时,我使用1 [&lt;AND (generated code)&gt;...] 它允许任意链接,但我不明白你所看到的如何允许它。尽管如此,我仍然认为这可能是算法代码生成方案的结果。

【讨论】:

  • 猜测总比没有好,但请帮助我理解其背后的逻辑。如果我不使用 ActiveRecord::where 方法,我根本不会生成 where 子句,包括无操作。你在 Rails 中使用过1 AND ... 查询吗?我不确定为什么需要它以及它如何促进链接。
  • 我找到了那个,但我没有使用 CanCan :/.
【解决方案2】:

检查您是否在使用 active_record-acts_as。这就是我的问题。

将下面的行添加到您的 Gemfile:

gem 'active_record-acts_as', :git => 'https://github.com/hzamani/active_record-acts_as.git'

这只会拉取希望修复的最新版本的 Gem。为我工作。

【讨论】:

  • 我没有使用这个 gem,所以它不能成为奇怪 SQL 的来源。我刚刚添加了根据评论找到的其他信息。也许这会帮助某人回答这个问题。
【解决方案3】:

我认为您个人看到了红宝石的副作用。

我认为做你正在做的事情的更好方法是使用

2.0.0-p481@meri :008 > [*1..5]
 => [1, 2, 3, 4, 5]

User.where(id: [*1..5]).to_sql
"SELECT `users`.* FROM `users`  WHERE `users`.`id` IN (1, 2, 3, 4, 5)"

因为这会创建一个数组与一个具有类 Range 的元素 1 的数组。

使用显式范围来触发 AREL 中的 BETWEEN。

# with end element, i.e. exclude_end=false
2.0.0-p481@meri :013 > User.where(id: Range.new(1,5)).to_sql
=> "SELECT `users`.* FROM `users`  WHERE (`users`.`id` BETWEEN 1 AND 5)"

# without end element, i.e. exclude_end=true
2.0.0-p481@meri :022 > User.where(id: Range.new(1, 5, true)).to_sql
 => "SELECT `users`.* FROM `users`  WHERE (`users`.`id` >= 1 AND `users`.`id` < 5)"

【讨论】:

  • 这些也是为 ARel 条件指定范围的好方法。 @pdobb 解释了 OR 1=0 出现的原因。
【解决方案4】:

基于您发现的事实,[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 为 null 的用户(意识到这不是真正正确的 SQL 语法)。这就是我在尝试查找 id 在空集中的所有用户时所期望的(即,我们不要求 nil id 或 null id 或其他任何东西)。所以,在我看来,将1=0 的确切来源作为黑匣子留下一点是可以的。至少我们现在可以推断为什么数组内部的范围会导致它出现!

更新

我还发现,即使直接使用ARel,还是可以得到1=0

User.arel_table[:id].in([]).to_sql
# => "1=0"

【讨论】:

  • 谢谢!这确实解释了奇怪的行为。
【解决方案5】:

如果您关心控制您生成的查询以及 SQL 语言和数据库功能的全部功能,那么我建议您从 ActiveRecord/Arel 转移到 Sequel。

老实说,ActiveRecord 会给您带来更多的怪癖和令人恼火的时刻,尤其是当您超越简单的杂乱无章的查询时。当你开始愤怒地尝试查询数据时,可能需要在这里和那里连接几个连接表,然后意识到你确实需要连接条件或联合所有类型的查询。

它在查询生成和结果处理方面也明显更快、更可靠,并且更容易组合您想要的查询。与 arel 不同,它还具有您可以实际阅读的真实文档。

我只是希望我早点发现它,而不是坚持使用 rails 默认数据访问层。

【讨论】:

  • 这个库github.com/jeremyevans/sequel ?我以前没听说过,但我会去看看。
  • 是的,就是这样。还有 sequel-rails 集成 gem。如果您使用的是 postgres(我也强烈推荐),那么还有 sequel-pg gem,它提供了更快的结果处理和流式传输。就像任何事情一样,都有一个学习曲线,但你也可以用它做更多的事情。
猜你喜欢
  • 1970-01-01
  • 2017-02-05
  • 2013-10-20
  • 1970-01-01
  • 2021-02-24
  • 1970-01-01
  • 2012-02-26
  • 1970-01-01
  • 2016-04-29
相关资源
最近更新 更多