首页 文章

Arel Rails 4.2导致问题(绑定丢失)

提问于
浏览
19

我们最近从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 回答

  • 0

    我知道这个问题有点旧,但错误听起来很熟悉 . 我在存储库中有一些注释和解决方案,所以我想我会分享 .

    我们收到的错误是:

    PG :: ProtocolViolation:ERROR:绑定消息提供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 是我们有单个绑定值的地方 . 那个_2898991失去了 .

    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 . 自从发布此问题以来,它似乎没有任何更新 .

相关问题