brianhempel / active_record_union

UNIONs in ActiveRecord! Adds proper union and union_all methods to ActiveRecord::Relation.
Other
423 stars 41 forks source link

Fails on Rails 4.1 + PostgreSQL: bind supplies 2 parameters, but prepared statement requires 1 #8

Open glebm opened 8 years ago

glebm commented 8 years ago

https://travis-ci.org/thredded/thredded/jobs/121800665

ActiveRecord::StatementInvalid: PG::ProtocolViolation: ERROR: bind message supplies 2 parameters, but prepared statement "a253" requires 1 : SELECT "thredded_topics".* FROM ( (SELECT "thredded_topics".* FROM "thredded_topics" WHERE "thredded_topics"."messageboard_id" = $1 AND (to_tsvector('english', "thredded_topics"."title") @@ plainto_tsquery('english', 'Rando thread'))) UNION (SELECT "thredded_topics".* FROM "thredded_topics" INNER JOIN "thredded_posts" ON "thredded_posts"."postable_id" = "thredded_topics"."id" WHERE "thredded_topics"."messageboard_id" = $1 AND (to_tsvector('english', "thredded_posts"."content") @@ plainto_tsquery('english', 'Rando thread'))) ) "thredded_topics" ORDER BY "thredded_topics"."updated_at" DESC, "thredded_topics"."id" DESC LIMIT 50 OFFSET 0

Only getting this error on the specific combination of PostgreSQL + Rails 4.1. All the other builds are passing.

glebm commented 8 years ago

Thredded dropped 4.1 compatibility finally. If this gem doesn't support 4.1, the dependency version in gemspec should be bumped though.

brianhempel commented 8 years ago

I'm just going to add a note to the README since I can't say affirmatively which combos work and don't work.

ActiveRecordUnion is tested against Rails 4.2 and Rails 5.0. It may or may not work on Rails 4.0/4.1.

kapluni commented 8 years ago

I am seeing a similar issue on rails 4.2.6.

  Course Load (0.4ms)  SELECT "courses".* FROM ( (SELECT "courses".* FROM "courses" INNER JOIN "enrollments" ON "courses"."id" = "enrollments"."course_id" WHERE "enrollments"."user_id" = $1) UNION (SELECT "courses".* FROM "courses" WHERE 1=0 AND "courses"."open_to_institution" = $2) ) "courses"  [["user_id", 1], ["open_to_institution", "t"], ["user_id", 1], ["open_to_institution", "t"]]

PG::ProtocolViolation: ERROR:  bind message supplies 4 parameters, but prepared statement "a31" requires 2
: SELECT "courses".* FROM ( (SELECT "courses".* FROM "courses" INNER JOIN "enrollments" ON "courses"."id" = "enrollments"."course_id" WHERE "enrollments"."user_id" = $1) UNION (SELECT "courses".* FROM "courses" WHERE 1=0 AND "courses"."open_to_institution" = $2) ) "courses"' duration=55.75 view=0.00 db=5.57 params={"include"=>"courses", "id"=>"1"} log_level=exception timestamp=2016-08-09 13:34:43 UTC transaction_id=06def7abb5a345343b76 user_id=1 exception_message=ERROR:  bind message supplies 4 parameters, but prepared statement "a31" requires 2

user.rb:

  has_many :enrollments
  has_many :enrolled_courses, through: :enrollments

  def courses
    enrolled_courses.union(Course.open_to_institution(institution_ids))
  end

course.rb

class Course < ActiveRecord::Base
  include Taggable

  belongs_to :institution
  has_many :enrollments

  scope :open_to_institution, ->(institution_id) { where(institution_id: institution_id).where(open_to_institution: true) }
end
brianhempel commented 8 years ago

Oh, that looks like a real bug. Thanks for the detailed report.

brianhempel commented 8 years ago

@kapluni can you provide more insight into this error? I've tried to recreate the query you posted as well as the my_user.courses query and everything works fine. I am having no problems with Postgres.

  1. What version of the pg gem are you using? (I'm on 0.18.4)
  2. What version of active_record_union are you using? (Latest is 1.2.0)
  3. What exactly is the Ruby code that produces the problem query? It can't be my_user.courses because the SQL query you posted doesn't include institution_id anywhere...unless there's some weird eager loading or something going on.

With "group" == "course", "category" == "institution", this is what I get:

User.new(id: 1).groups.union(Group.in_category([1,2,3])).to_a
Group Load (0.5ms) 
  SELECT "groups".* FROM (
    (SELECT "groups".* FROM "groups" INNER JOIN "enrollments"
      ON "groups"."id" = "enrollments"."group_id"
      WHERE "enrollments"."user_id" = $1 AND (1=0))
    UNION
    (SELECT "groups".* FROM "groups"
      WHERE "groups"."category_id" IN (1, 2, 3)
      AND "groups"."public" = $2)
  ) "groups"
  [["user_id", 1], ["public", "t"]]
class User < ActiveRecord::Base
  has_many :enrollments
  has_many :groups, through: :enrollments
end

class Enrollment < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end

class Group < ActiveRecord::Base
  belongs_to :category
  has_many :enrollments

  scope :in_category, ->(category_id) { where(category: category_id).where(public: true) }
end

class Category < ActiveRecord::Base
  has_many :groups
end
kapluni commented 8 years ago

sorry for the slow respone

What version of the pg gem are you using? (I'm on 0.18.4)

same

What version of active_record_union are you using? (Latest is 1.2.0)

same

What exactly is the Ruby code that produces the problem query? It can't be my_user.courses because the SQL query you posted doesn't include institution_id anywhere...unless there's some weird eager loading or something going on.

i am using jsonapi-resources , and it's loading the relationship as a related resource, via:

 define_method foreign_key do
              records = public_send(associated_records_method_name)
              return records.collect do |record|
                record.public_send(relationship.resource_klass._primary_key)
              end
            end unless method_defined?(foreign_key)

record in this case would be the user, and course is the relationship.

i just ran into a similar case with another, similar pattern in my code.

thanks very much for looking into this!