Arel + Rails 4.2 导致问题(绑定丢失)
Posted
技术标签:
【中文标题】Arel + Rails 4.2 导致问题(绑定丢失)【英文标题】:Arel + Rails 4.2 causing problems (bindings being lost) 【发布时间】:2016-02-12 00:22:53 【问题描述】:我们最近从 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)
每当我们运行这部分查询时,绑定都会丢失。 first 和 second 都是活动记录对象,但一旦我们“联合”它们,它们就会失去绑定,成为 arel 对象。
我们尝试循环查询并手动替换绑定,但似乎无法使其正常工作。我们应该怎么做?
编辑:
我们还尝试从 first 和 second 中提取绑定值,然后在 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 中建议的解决方案。
【问题讨论】:
我认为有些查询可以写得更好(例如:second = Customer.joins(:import_logs).where(import_logs: /* ImportLog conditions here */ ))... 我没有得到你想要完成的事情。 我遇到了同样的问题。请参阅此帖子的已接受答案:***.com/questions/57491185/… 将a.union(b)
替换为 Arel::Nodes::UnionAll.new(a, b)
为我解决了绑定问题。
【参考方案1】:
我知道这个问题有点老了,但这个错误听起来很熟悉。我在存储库中有一些笔记和我们的解决方案,所以我想分享一下。
我们收到的错误是:
PG::ProtocolViolation:错误:绑定消息提供 0 个参数,但是 准备好的语句 "" 需要 1
如您所见,我们的情况有点不同。我们没有 8 个绑定值。然而,我们的单一绑定值仍然被破坏。我更改了事物的命名以保持通用性。
first_level = Blog.all_comments
second_level = Comment.where(comment_id: first_level.select(:id))
third_level = Comment.where(comment_id: second_level.select(:id))
Blog.all_comments
是我们拥有单个绑定值的地方。这就是我们正在失去的部分。
union = first_level.union second_level
union2 = Comment.from(
Comment.arel_table.create_table_alias union, :comments
).union third_level
relation = Comment.from(Comment.arel_table.create_table_alias union2, :comments)
我们创建了一个与您非常相似的联合,只是我们需要联合三个不同的查询。
为了在此时获取丢失的绑定值,我们做了一个简单的赋值。最后,这比你的情况要简单一些。不过,它可能会有所帮助。
relation.bind_values = first_level.bind_values
relation
顺便说一下,这是我们在处理此问题时发现的GitHub issue。自发布此问题以来,它似乎没有任何更新。
【讨论】:
以上是关于Arel + Rails 4.2 导致问题(绑定丢失)的主要内容,如果未能解决你的问题,请参考以下文章
Rails/Arel:选择所有记录作为 ActiveRecord::Relation