【问题标题】:Sanitizing SQL in Rails where conditions may be NULL在条件可能为 NULL 的 Rails 中清理 SQL
【发布时间】:2015-03-16 02:36:34
【问题描述】:

我正在努力清理原始 SQL 查询,其中 WHERE 条件可能具有值或为 NULL。我希望使用 Active Record 的内置消毒剂...

(注意:我将使用一个简化查询来进行演示——我们真正的查询是一个跨不同模型类型的复杂 UNION,很难使用AR查询界面)

尝试 1:

raw_query = "SELECT * FROM folders WHERE user_id = ? AND parent_id = ?"
sanitized_query = ActiveRecord::Base.send(:sanitize_sql_array, [raw_query, current_user.id, params[:parent_id]])
results = ActiveRecord::Base.connection.execute(sanitized_query);

但如果 params[:parent_id] 为 nil,则 sanitized_query 最终为 SELECT * FROM folders WHERE user_id = 1 AND parent_id = NULL 这是无效的,因为parent_id = NULL 应该是parent_id IS NULL

尝试 2:

然后我找到了sanitize_sql_hash 方法,它似乎非常适合构建条件:

sanitized_conditions = ActiveRecord::Base.send(:sanitize_sql_hash, {user_id: current_user.id, parent_id: params[:parent_id]})
sanitized_query = "SELECT * FROM folders WHERE #{sanitized_conditions}"
results = ActiveRecord::Base.connection.execute(sanitized_query);

但第一行失败:

NoMethodError: 未定义的方法 `abstract_class?'对象:类

该方法也被列为已弃用,并将在 Rails 5 中删除,但它正是我正在寻找的。还有其他方法可以从值的哈希中生成安全的 WHERE 条件吗?

【问题讨论】:

标签: sql ruby-on-rails activerecord sanitization


【解决方案1】:

看起来错误是在https://github.com/rails/rails/blob/7bb620869725ad6de603f6a5393ee17df13aa96c/activerecord/lib/active_record/model_schema.rb#L160reset_table_name 方法中引发的,所以这个方法可能不适用于 ActiveRecord::Base 类。

假设你有一个文件夹的模型类,这应该可以工作:

Folder.send(:sanitize_sql_hash, {user_id: current_user.id, parent_id: params[:parent_id]})

在快速测试中,我得到了类似的结果:

'"folders"."user_id" = 123 AND "folders"."parent_id" IS NULL'

【讨论】:

  • 谢谢,这个 确实 工作现在 - 烦人的是 sanitize_sql_hash 在 Rails 5 中被删除,这意味着我们不能将其用作永久解决方案......
  • 是的,升级到 Rails 5 会很有趣。也许那时会有更好的选择,否则我能想到的唯一选择是使用to_sql 方法生成整个查询然后提取where 子句,例如Folder.where(...).to_sql.split('WHERE ')[1]
  • @Steve- 是的,从生成的查询中提取 where 子句实际上并不是一个坏主意-感谢您的帮助!
  • 我为 where 子句提取创建了一个猴子补丁:answer
【解决方案2】:

我使用@Steve 的trick 创建了一个已弃用但非常需要的sanitize_sql_hash_for_conditions 的Rails 5 安全版本:

class ActiveRecord::Base 

  # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
  #
  # (This is an alternative to sanitize_sql_hash_for_conditions, which was deprecated in Rails 5.
  # SEE: https://api.rubyonrails.org/v4.2/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql_hash_for_conditions)
  # 
  # This is needed because the latest SQL sanitization methods cannot handle conditions where an attribute is nil.

  def self.sanitize_sql_hash_for_conditions_alt(attrs)
    self.where(attrs).to_sql.split('WHERE ')[1]
  end

end

在代表您所针对的表的任何 AR 模型类上调用它(而不是直接在 ActiveRecord::Base 上)。

Folder.sanitize_sql_hash_for_conditions_alt({user_id: 1, parent_id: 无})
=> "\"folders\".\"user_id\" = 1 AND \"folders\".\"parent_id\" IS NULL"

【讨论】:

    猜你喜欢
    • 2022-11-23
    • 2011-11-01
    • 1970-01-01
    • 2011-03-01
    • 2016-03-27
    • 2012-10-16
    • 2013-07-31
    • 1970-01-01
    • 2010-11-25
    相关资源
    最近更新 更多