我们最近从Rails 4.1升级到Rails 4.2,并且看到使用Arel Activerecord时出现问题,因为我们遇到了这种类型的错误:
ActiveRecord::StatementInvalid: PG::ProtocolViolation: ERROR: bind message supplies 0 parameters, but prepared statement "" requires 8
这是破坏的代码:
customers = Customer.arel_table
ne_subquery = ImportLog.where(
importable_type: Customer.to_s,
importable_id: customers['id'],
remote_type: remote_type.to_s.singularize,
destination: 'hello'
).exists.not
first = Customer.where(ne_subquery).where(company_id: @company.id)
second = Customer.joins(:import_logs).merge(
ImportLog.where(
importable_type: Customer.to_s,
importable_id: customers['id'],
remote_type: remote_type.to_s.singularize,
status: 'pending',
destination: 'hello',
remote_id: nil
)
).where(company_id: @company.id)
Customer.from(
customers.create_table_alias(
first.union(second),
Customer.table_name
)
)
我们想出了如何通过将exists.not移动到Customer.where中来解决查询的第一部分(运行到没有绑定的相同rails错误),如下所示:
ne_subquery = ImportLog.where(
importable_type: Customer.to_s,
importable_id: customers['id'],
destination: 'hello'
)
first = Customer.where("NOT (EXISTS (#{ne_subquery.to_sql}))").where(company_id: @company.id)
这似乎有效,但我们遇到了与这行代码相同的问题:
first.union(second)
每当我们运行这部分查询时,绑定就会丢失 . 第一个和第二个都是活动记录对象,但只要我们“联合”它们,它们就会失去绑定成为arel对象 .
我们尝试循环查询并手动替换绑定但似乎无法使其正常工作 . 我们该怎么做呢?
编辑:
我们还尝试从第一个和第二个中提取绑定值,然后在arel对象中手动替换它们,如下所示:
union.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
bv = bind_values[i]
bp.replace(Customer.connection.substitute_at(bv, i))
end
但是,它失败了,因为:
NoMethodError: undefined method `replace' for #<Arel::Nodes::BindParam:0x007f8aba6cc248>
这是rails github repo中建议的解决方案 .
1 回答
我知道这个问题有点旧,但错误听起来很熟悉 . 我在存储库中有一些注释和解决方案,所以我想我会分享 .
我们收到的错误是:
你可以看到,我们的情况有点不同 . 我们没有8个绑定值 . 但是,我们的单个绑定值仍然被破坏 . 我改变了事物的命名以保持一般性 .
Blog.all_comments
是我们有单个绑定值的地方 . 那个_2898991失去了 .我们创建了一个与你很相似的联盟,除了我们需要结合三个不同的查询 .
为了获得此时丢失的绑定值,我们做了一个简单的赋值 . 最后,这比一个案例更简单 . 但是,它可能会有所帮助 .
顺便说一句,这是我们在研究这个问题时发现的GitHub issue . 自从发布此问题以来,它似乎没有任何更新 .