清理条件可能为 NULL 的 Rails 中的 SQL
Sanitizing SQL in Rails where conditions may be NULL
我正在努力清理原始 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]
为零,则 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: undefined method `abstract_class?' for Object:Class
该方法也被列为已弃用,将在 Rails 5 中删除,但这正是我要找的。是否有另一种方法可以从值的散列生成安全的 WHERE 条件?
看起来错误是在 https://github.com/rails/rails/blob/7bb620869725ad6de603f6a5393ee17df13aa96c/activerecord/lib/active_record/model_schema.rb#L160 的 reset_table_name
方法中抛出的,所以可能此方法不适用于 ActiveRecord::Base class。
假设您有文件夹的模型 class 这应该有效:
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'
我创建了一个 Rails 已弃用但非常需要的 5 安全版本 sanitize_sql_hash_for_conditions, using @Steve's :
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
在任何代表 table 的 AR 模型 class 上调用它(而不是直接在 ActiveRecord::Base 上)。
Folder.sanitize_sql_hash_for_conditions_alt({user_id: 1, parent_id:
nil})
=> "\"folders\".\"user_id\" = 1 AND \"folders\".\"parent_id\" IS NULL"
我正在努力清理原始 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]
为零,则 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: undefined method `abstract_class?' for Object:Class
该方法也被列为已弃用,将在 Rails 5 中删除,但这正是我要找的。是否有另一种方法可以从值的散列生成安全的 WHERE 条件?
看起来错误是在 https://github.com/rails/rails/blob/7bb620869725ad6de603f6a5393ee17df13aa96c/activerecord/lib/active_record/model_schema.rb#L160 的 reset_table_name
方法中抛出的,所以可能此方法不适用于 ActiveRecord::Base class。
假设您有文件夹的模型 class 这应该有效:
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'
我创建了一个 Rails 已弃用但非常需要的 5 安全版本 sanitize_sql_hash_for_conditions, using @Steve's
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
在任何代表 table 的 AR 模型 class 上调用它(而不是直接在 ActiveRecord::Base 上)。
Folder.sanitize_sql_hash_for_conditions_alt({user_id: 1, parent_id: nil})
=> "\"folders\".\"user_id\" = 1 AND \"folders\".\"parent_id\" IS NULL"