使用ActiveRecord转义查询对象中的参数?

时间:2015-02-05 00:47:10

标签: ruby activerecord

给定查询对象(不是AR模型)

class ComplexQuery
  QUERY = <<-SQL
    ...
  SQL

  def new(param1, param2)
    ...
  end

  def execute
    # format and interpolate parameters into QUERY
    # pass finished SQL to `execute` or `select_all`
  end
end

我如何方便地转义所有参数?

我已经成功完成了三种技术,但没有一种方便。

  1. 使用raw_connection(对我而言)返回PG::Conn的实例并致电exec_params。我对此不满意,因为exec_params需要一组冗长的参数来指定数据类型。
  2. 在我的查询对象中使用
  3. include ActiveRecord::Sanitization并使用其中一种方便的方法,例如replace_named_bind_variables。我对此不满意,因为replace_named_bind_variablesprotected,我必须使用send
  4. 改为写module。出于某种原因,当我include ActiveRecord::Sanitization进入模块时,我能够使用其受保护的方法。我对此不满意,因为我想在不执行查询对象的情况下实例化我的查询对象,例如用于测试。
  5. ActiveRecord::Sanitization纳入class感觉是最好的解决方案,但我必须做错事,因为我应该能够使用protected方法。

    我正在寻找

    的解决方案
    1. 转义多个参数
    2. 旨在供ActiveRecord消费者使用
    3. 推断参数的数据类型并相应地格式化
    4. 我能够找到一些相关的问题

3 个答案:

答案 0 :(得分:6)

最好的方法可能是使用原始Postgres驱动程序创建一个准备好的语句。不幸的是,ActiveRecord没有公开这样做的方法。他们可能很快就会添加它,因为mysql2支持预处理语句。但与此同时,这里有如何使用rails中的原始PG驱动程序。

http://deveiate.org/code/pg/PG/Connection.html#method-i-prepare

conn = ActiveRecord::Base.connection.raw_connection
conn.prepare('my_query', 'SELECT foo FROM bar WHERE baz=$1 OR baz=$2')
result = conn.exec_prepared('my_query', ['param1', 'param2'])

请注意使用$作为符号来表示位置参数。这些数字对应于您传递给exec_prepared的数组中参数的位置。

答案 1 :(得分:2)

所有清理方法都是already included in ActiveRecord::Base作为类方法,并且应该以{{1​​}}运行。

原因是所有ActiveRecord::Base#sanitize_***函数都是特定于驱动程序的,并且依赖于sanitize_*对象,该对象显然来自ActiveRecord::ConnectionHandling

connection合同强制ActiveSupport::Concern模块内容成为包含Sanitization::ClassMethods的类/模块的类方法,这就是它们在开放Sanitization派生类中可用的方式。它们不是公开的[恕我直言],因为它们需要建立连接,这通常适用于后代,而ActiveRecord::Base本身可能不是这样。

一般来说,没有明确的方法来做没有连接的问题(例如,不在ActiveRecord::Base后代。)postgres的引用与mysql的引用不同。

总结:包括ActiveRecord::Base到一个类/模块中没有多大意义,因为整个功能已经在ActiveRecord::Sanitization中呈现。很快,由于想要在没有实际连接的情况下使用这些引用功能,她可能会实现自己的引用函数,映射到相应的ActiveRecord::Base

对于应该建立连接的情况,我会先将一个包装模块添加到ConnectionAdapter#quote,将受保护的函数包装到public(在此模块中,受保护的函数将可用。)

希望这有帮助。

答案 2 :(得分:0)

ActiveRecord::Base.connection.quote方法可能对其他在rails中编写原始sql的人有所帮助。

它通过引用字符串来防止SQL注入,还强制转换用于原始sql的值,例如将nil(NilClass)转换为“ NULL”,将true(TrueClass)转换为“ TRUE”。

使用ActiveRecord::Base.connection.quote @justinhoward的答案可以重写为

def quote_value(value)
  ActiveRecord::Base.connection.quote(value)
end

ActiveRecord::Base.connection('SELECT foo FROM bar WHERE baz=#{quote_value(param1)} OR baz=#{quote_value(param2)}')