5

我正在开发一个基本的 Rails 4.0 应用程序以了解它是如何工作的,但我遇到了一些我似乎无法弄清楚的事情。我一直在通过 ActiveRecord 对默认的 Sqlite DB 进行查询,对于大多数查询,根据调试输出,它似乎会生成参数化查询,如下所示:

2.0.0-p247 :070 > file.save
   (0.2ms)  begin transaction
   SQL (0.6ms)  UPDATE "rep_files" SET "report_id" = ?, "file_name" = ?, "updated_at" = ?
   WHERE "rep_files"."id" = 275  [["report_id", 3], ["file_name", "hello.jpg"],
   ["updated_at", Mon, 09 Sep 2013 04:30:19 UTC +00:00]]
   (28.8ms)  commit transaction

但是,每当我使用 find_by 进行查询时,似乎只是将提供的参数粘贴到生成的 SQL 中:

2.0.0-p247 :063 > file = RepFile.find_by(report_id: "29", file_name: "1.png")
  RepFile Load (6.2ms)  SELECT "rep_files".* FROM "rep_files" WHERE
  "rep_files"."report_id" = 29 AND "rep_files"."file_name" = '1.png' LIMIT 1

它似乎确实正确地转义了参数以防止 SQL 注入:

2.0.0-p247 :066 > file = RepFile.find_by(report_id: "29", file_name: "';")
  RepFile Load (0.3ms)  SELECT "rep_files".* FROM "rep_files" WHERE
  "rep_files"."report_id" = 29 AND "rep_files"."file_name" = ''';' LIMIT 1

但是,据我了解,将参数化查询发送到数据库被认为是比尝试转义查询字符串更好的选择,因为参数化选项将导致查询数据完全绕过数据库的解析引擎。

那么这里发生了什么?这是 Sqlite 适配器中的一些奇怪之处还是生成调试输出的方式?如果 ActiveRecord 确实是这样工作的,有什么原因吗?我在任何地方都找不到有关此的任何信息。我已经开始查看 ActiveRecord 代码,但还没有弄清楚任何事情。

4

1 回答 1

3

如果我们查看find_by源代码,我们会看到:

def find_by(*args)
  where(*args).take
end

take只是将附加limit 1到查询上,所以我们剩下where. 该where方法可以处理具有各种占位符格式的各种形式的参数,特别是,您可以where这样调用:

where('c = :pancakes', :pancakes => 6)

当您有一个最好用 SQL 片段表示的复杂查询或多次使用相同值的查询时,使用命名占位符非常好,因此命名占位符是一个非常有价值的功能。此外,您可以应用where从调用ActiveRecord::Relation中获得where的查询,并且可以构建最终查询,这些查询分散在多个彼此不了解的方法和范围内。所以,where有一个问题:相互不了解的多个事物可以使用相同的命名占位符,并且可能会出现冲突。解决此问题的一种方法是重命名命名占位符以确保唯一性,另一种方法是通过字符串整理手动填充占位符。另一个问题是不同的数据库支持不同的占位符语法。ActiveRecord 已选择手动填写占位符。

摘要:find_by不使用占位符是因为where不使用占位符,因为where通过字符串插值构建查询比跟踪所有占位符和特定于数据库的语法更容易。

于 2013-09-10T04:50:20.683 回答