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