chr*_*son 12 postgresql activerecord ruby-on-rails rails-activerecord
我在我的应用程序中没有日期查询时花了很多时间,我想抽出一些问题.
所以说我有一个带有DateTime starts_at字段的模型:
Shift.where('starts_at::time > ?', '20:31:00.00')
-> SELECT "shifts".* FROM "shifts" WHERE (starts_at::time > '20:31:00.00')
Run Code Online (Sandbox Code Playgroud)
这正确地返回大于时间20:31的所有'starts_at'值.
我想动态地将列名传递给查询,所以我可以这样做:
Shift.where('? > ?', "#{column_name}::time", '20:31:00.00').
-> SELECT "shifts".* FROM "shifts" WHERE ('starts_at::time' > '20:31:00.00')
Run Code Online (Sandbox Code Playgroud)
在此示例中,这不起作用,因为搜索starts_at::time作为字符串执行,而不是作为具有强制转换的列time.
如何使用强制column_name转换安全地传入查询::time?虽然这不会接受用户输入,但我仍然希望确保考虑SQL注入.
mu *_*ort 20
这是更复杂的比你想象的,在第一,因为标识符(列名,表名,...)和价值('pancakes',6,...),在SQL非常不同的东西有不同的引用规则,甚至引号字符(单引号对于字符串,标准SQL中标识符的双引号,MySQL中标识符的反引号,SQL-Server中标识符的括号,......).如果您考虑像Ruby变量名称这样的标识符,以及类似的文字Ruby值,那么您可以开始看到它们之间的区别.
当你这样说:
where('? > ?', ...)
Run Code Online (Sandbox Code Playgroud)
两个占位符都将被视为值(不是标识符)并被引用.为什么是这样?ActiveRecord无法知道哪个?应该是标识符(例如created_at列名),哪个应该是值(例如20:31:00.00).
数据库连接确实有一个专门用于引用列名的方法:
> puts ActiveRecord::Base.connection.quote_column_name('pancakes')
"pancakes"
=> nil
Run Code Online (Sandbox Code Playgroud)
所以你可以这样说:
quoted_column = Shift.connection.quote_column_name(column_name)
Shift.where("#{quoted_name}::time > ?", '20:31:00.00')
Run Code Online (Sandbox Code Playgroud)
这有点令人不快,因为我们在使用字符串插值来构建SQL时会退缩(或至少我们应该).但是,quote_column_name会照顾任何狡猾或不安全的东西,column_name所以这实际上并不危险.
你也可以说:
quoted_column = "#{Shift.connection.quote_column_name(column_name)}::time"
Shift.where("#{quoted_name} > ?", '20:31:00.00')
Run Code Online (Sandbox Code Playgroud)
如果你不总是需要将列名转换为a time.甚至:
clause = "#{Shift.connection.quote_column_name(column_name)}::time > ?"
Shift.where(clause, '20:31:00.00')
Run Code Online (Sandbox Code Playgroud)
您也可以使用extract或使用其他日期/时间函数而不是类型转换,但您仍然会留下引用问题和有点畏缩的quote_column_name电话.
另一种选择是白名单,column_name以便只允许特定的有效值.然后你可以将保险箱column_name放入查询中:
if(!in_the_whitelist(column_name))
# Throw a tantrum, hissy fit, or complain in your preferred fashion
end
Shift.where("#{column_name} > ?", '20:31:00.00')
Run Code Online (Sandbox Code Playgroud)
只要您没有任何时髦的列名"gotta have some breakfast"或类似的东西总是需要正确引用,这应该没问题.您甚至可以使用Shift.column_names或Shift.columns构建白名单.
使用白名单然后quote_column_name可能是最安全的,但该quote_column_name方法应该足够了.
| 归档时间: |
|
| 查看次数: |
3017 次 |
| 最近记录: |