给定查询对象(不是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
我如何方便地转义所有参数?
我已经成功完成了三种技术,但没有一种方便。
raw_connection
(对我而言)返回PG::Conn
的实例并致电exec_params
。我对此不满意,因为exec_params
需要一组冗长的参数来指定数据类型。include ActiveRecord::Sanitization
并使用其中一种方便的方法,例如replace_named_bind_variables
。我对此不满意,因为replace_named_bind_variables
是protected
,我必须使用send
。 module
。出于某种原因,当我include ActiveRecord::Sanitization
进入模块时,我能够使用其受保护的方法。我对此不满意,因为我想在不执行查询对象的情况下实例化我的查询对象,例如用于测试。将ActiveRecord::Sanitization
纳入class
感觉是最好的解决方案,但我必须做错事,因为我应该能够使用protected
方法。
我正在寻找:
的解决方案我能够找到一些相关的问题
send
使用私有方法。sanitize_sql_array
,但我的问题是关于单独的查询对象,而不是AR模型。答案 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)}')