Arel + Rails 4.2 вызывает проблемы (привязки теряются)

Недавно мы обновили Rails 4.2 от Rails 4.1 и видим проблемы с использованием 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
        )
      )

Мы выяснили, как решить первую часть запроса (запускаясь в ту же ошибку рельсов, не имеющих привязок), перемещая существующий. Не для того, чтобы быть внутри Customer.where:

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)

всякий раз, когда мы запускаем эту часть запроса, привязки теряются. первый и второй являются активными объектами записи, но как только мы "объединяем" их, они теряют привязки, становятся объектами isl.

Мы пробовали циклически выполнять запрос и вручную заменяли привязки, но не могли заставить его работать исправно. Что мы должны делать вместо этого?

EDIT:

Мы также попытались извлечь значения привязки с первого и второго и затем вручную заменить их в объекте isl следующим образом:

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>

Это решение было предложено в рельсах github repo.

Ответы

Ответ 1

Я знаю, что этот вопрос немного стар, но ошибка звучит знакомо. У меня были некоторые заметки и наше решение в репозитории, поэтому я решил поделиться с вами.

Ошибка, которую мы получали, была:

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 - это где мы имеем единственное значение привязки. То, что мы теряем.

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 мы обнаружили, работая над этим. Кажется, у него нет обновлений, поскольку этот вопрос был опубликован.