Skip to content
This repository has been archived by the owner on Mar 30, 2022. It is now read-only.

Join with where block and default is broken. #361

Open
jorgevaldivia opened this issue Feb 11, 2015 · 1 comment · May be fixed by #362
Open

Join with where block and default is broken. #361

jorgevaldivia opened this issue Feb 11, 2015 · 1 comment · May be fixed by #362

Comments

@jorgevaldivia
Copy link

Squeel appears to be broken when defining an association with a where clause of type String, and a default_scope on one of the models.

I'm using rails 4.1.9, though I've seen it on other versions > 4 as well. Using squeel 1.2.3 and postgres.

Models

class Vehicle < ActiveRecord::Base
  # Specify a simple string based where clause on the association
  has_many :vehicle_assignments, -> { where("vehicles.id = 1") }
end

class VehicleAssignment < ActiveRecord::Base
  belongs_to :vehicle

  # Add a simple default scope
  default_scope lambda {
    where(id: 1)
  }
end

Code

 Vehicle.joins(:vehicle_assignments)

Result

PG::SyntaxError: ERROR:  syntax error at or near "="
LINE 1: ..."."id" AND "vehicle_assignments"."id" = 1, (vehicles.id = 1)

SQL

SELECT "vehicles".* FROM "vehicles" INNER JOIN "vehicle_assignments" ON
"vehicle_assignments"."vehicle_id" = "vehicles"."id" AND "vehicle_assignments"."id" = 1, (vehicles.id = 1)

As you can see, the last conditional is not being added to the SQL correctly. We expect AND (vehicles.id = 1) but get , (vehicles.id = 1). My assumption is that this occurs because vehicles.id = 1 is being added as a simple simple string. If we change the syntax to "vehicles.id" => 1 then everything works as expected.

This only occurs when there is both a string based where scope block in the association definition, and when the association's model has a default_scope. The error occurs even when not using squeel syntax anywhere in the app.

I've traced the problem to the collapse_wheres method, specifically in the final code block where String based where clauses are explicitly handled. String based where clauses are added to arel.where but they are never ANDed with the other groups. I'll add a pull request with what I think is a correct solution, though I will mention that I am not deeply versed in arel.

Any input would be greatly appreciated.

@vellotis
Copy link

I have been using squeel long time already. So I thought that it is Active Record issue. But after I made test for it I reconsidered it as it didn't occur if squeel wasn't required.

# Activate the gem you are reporting the issue against.
gem 'activerecord', '4.1.9'
gem 'minitest'
gem 'logger'
gem 'squeel', '1.2.2'
require 'active_record'
require 'minitest/autorun'
require 'logger'
require 'squeel'

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection({
  adapter: 'mysql2',
  database: 'kliendikaardid_test',
  username: 'vagrant',
  password: 'vagrant',
  host: 'localhost'
})
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :trees, force: true do |t|
  end

  create_table :branches, force: true do |t|
    t.belongs_to :tree
  end

  create_table :leafs, force: true do |t|
    t.belongs_to :branch
    t.boolean    :broken
    t.boolean    :some_bool
  end
end

module SomeModule

  def default_scope_from_module
    default_scope { where('leafs.broken IS NULL') }
  end

end

ActiveRecord::Base.send :extend, SomeModule

class Tree < ActiveRecord::Base
  has_many :branches
end

class Branch < ActiveRecord::Base
  belongs_to :tree, inverse_of: :branches
  has_many :leafs
end

class Leaf < ActiveRecord::Base
  default_scope_from_module
  default_scope { where('leafs.some_bool = 1') }

  belongs_to :branch, inverse_of: :leafs
end

class BugTest < Minitest::Test
  def test_default_scope_from_extend
    tree = Tree.create!()
    tree.branches.joins(:leafs).each do ||
      # error already met before this step
    end
  end
end

If require 'squeel' is included the following exception will be thrown:

ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1) WHERE `branches`.`tree_id` = 1' at line 1: SELECT `branches`.* FROM `branches` INNER JOIN `leafs` ON `leafs`.`branch_id` = `branches`.`id` AND (leafs.broken IS NULL), (leafs.some_bool = 1) WHERE `branches`.`tree_id` = 1

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants