我正在开发一个基本的 Rails 4.0 应用程序以了解它是如何工作的,但我遇到了一些我似乎无法弄清楚的事情。我一直在通过 ActiveRecord 查询默认的 Sqlite 数据库,对于大多数查询,根据调试输出,它似乎生成参数化查询,如下所示:
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 注入(inject):
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 代码,但还没有找到任何答案。
最佳答案
如果我们看find_by
in the source ,我们看到了:
def find_by(*args)
where(*args).take
end
take
只是将 limit 1
添加到查询中,所以我们只剩下 where
。 where
方法可以处理具有各种占位符格式的各种形式的参数,具体来说,您可以这样调用where
:
where('c = :pancakes', :pancakes => 6)
当您有一个最好用 SQL 片段表达的复杂查询或多次使用相同值的查询时,使用命名占位符非常有用,因此命名占位符是一个非常有值(value)的功能。此外,您可以将 where
应用于从 where
调用中获得的 ActiveRecord::Relation
,然后您可以分段构建最终查询跨多个彼此不了解的方法和范围。因此,where
有一个问题:多个相互不了解的事物可以使用相同的命名占位符,并且可能会出现冲突。解决此问题的一种方法是重命名命名占位符以确保唯一性,另一种方法是通过字符串整理手动填充占位符。另一个问题是不同的数据库支持不同的占位符语法。 ActiveRecord 已选择手动填写占位符。
总结:find_by
不使用占位符,因为 where
不使用,where
不使用,因为它更容易构建查询通过字符串插值零碎地跟踪所有占位符和特定于数据库的语法。
关于sql - 为什么 ActiveRecord 会为大多数操作生成参数化查询,但不会为 find_by 生成参数化查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18710131/