aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb14
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb3
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb18
-rw-r--r--activerecord/lib/active_record/relation.rb4
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb33
-rw-r--r--activerecord/lib/active_record/relation/merger.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb2
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb8
10 files changed, 54 insertions, 36 deletions
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 096f016976..358cdabc7f 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -6,22 +6,16 @@ module ActiveRecord
module Associations
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
class AliasTracker # :nodoc:
- def self.create(connection, initial_table)
- aliases = Hash.new(0)
- aliases[initial_table] = 1
- new(connection, aliases)
- end
-
- def self.create_with_joins(connection, initial_table, joins)
+ def self.create(connection, initial_table, joins)
if joins.empty?
- create(connection, initial_table)
+ aliases = Hash.new(0)
else
aliases = Hash.new { |h, k|
h[k] = initial_count_for(connection, k, joins)
}
- aliases[initial_table] = 1
- new(connection, aliases)
end
+ aliases[initial_table] = 1
+ new(connection, aliases)
end
def self.initial_count_for(connection, name, table_joins)
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 6118cef913..11967e0571 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -23,8 +23,7 @@ module ActiveRecord
reflection = association.reflection
scope = klass.unscoped
owner = association.owner
- alias_tracker = AliasTracker.create(klass.connection, scope.table.name)
- chain = get_chain(reflection, association, alias_tracker)
+ chain = get_chain(reflection, association, scope.alias_tracker)
scope.extending! reflection.extensions
add_constraints(scope, owner, chain)
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 23741b2f6a..b70b680885 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -90,8 +90,8 @@ module ActiveRecord
# associations # => [:appointments]
# joins # => []
#
- def initialize(base, table, associations, joins, eager_loading: true)
- @alias_tracker = AliasTracker.create_with_joins(base.connection, table.name, joins)
+ def initialize(base, table, associations, alias_tracker, eager_loading: true)
+ @alias_tracker = alias_tracker
@eager_loading = eager_loading
tree = self.class.make_tree associations
@join_root = JoinBase.new(base, table, build(tree, base))
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 29542f917e..508132accb 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -183,13 +183,25 @@ module ActiveRecord
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
+ # Require the adapter itself and give useful feedback about
+ # 1. Missing adapter gems and
+ # 2. Adapter gems' missing dependencies.
path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
begin
require path_to_adapter
- rescue Gem::LoadError => e
- raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)."
rescue LoadError => e
- raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
+ # We couldn't require the adapter itself. Raise an exception that
+ # points out config typos and missing gems.
+ if e.path == path_to_adapter
+ # We can assume that a non-builtin adapter was specified, so it's
+ # either misspelled or missing from Gemfile.
+ raise e.class, "Could not load the '#{spec[:adapter]}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
+
+ # Bubbled up from the adapter require. Prefix the exception message
+ # with some guidance about how to address it and reraise.
+ else
+ raise e.class, "Error loading the '#{spec[:adapter]}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
+ end
end
adapter_method = "#{spec[:adapter]}_connection"
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 3517091a6e..1f59a0c53a 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -551,6 +551,10 @@ module ActiveRecord
limit_value || offset_value
end
+ def alias_tracker(joins = []) # :nodoc:
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
+ end
+
protected
def load_records(records)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index c92d5a52f4..6e07402979 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -310,12 +310,12 @@ module ActiveRecord
return false if !conditions || limit_value == 0
- relation = self unless eager_loading?
- relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false))
-
- return false if ActiveRecord::NullRelation === relation
+ if eager_loading?
+ relation = apply_join_dependency(construct_join_dependency(eager_loading: false))
+ return relation.exists?(conditions)
+ end
- relation = construct_relation_for_exists(relation, conditions)
+ relation = construct_relation_for_exists(conditions)
skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false
rescue ::RangeError
@@ -366,17 +366,16 @@ module ActiveRecord
# preexisting join in joins_values to categorizations (by way of
# the `has_many :through` for categories).
#
- join_dependency = construct_join_dependency(joins_values)
+ join_dependency = construct_join_dependency
- aliases = join_dependency.aliases
- relation = select aliases.columns
- relation = apply_join_dependency(relation, join_dependency)
+ relation = apply_join_dependency(join_dependency)
+ relation._select!(join_dependency.aliases.columns)
yield relation, join_dependency
end
- def construct_relation_for_exists(relation, conditions)
- relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
+ def construct_relation_for_exists(conditions)
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
case conditions
when Array, Hash
@@ -388,17 +387,19 @@ module ActiveRecord
relation
end
- def construct_join_dependency(joins = [], eager_loading: true)
+ def construct_join_dependency(eager_loading: true)
including = eager_load_values + includes_values
- ActiveRecord::Associations::JoinDependency.new(klass, table, including, joins, eager_loading: eager_loading)
+ ActiveRecord::Associations::JoinDependency.new(
+ klass, table, including, alias_tracker(joins_values), eager_loading: eager_loading
+ )
end
def construct_relation_for_association_calculations
- apply_join_dependency(self, construct_join_dependency(joins_values))
+ apply_join_dependency(construct_join_dependency)
end
- def apply_join_dependency(relation, join_dependency)
- relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
+ def apply_join_dependency(join_dependency)
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
if using_limitable_reflections?(join_dependency.reflections)
relation
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 03824ffff9..ebc72d28fd 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -122,7 +122,7 @@ module ActiveRecord
end
join_dependency = ActiveRecord::Associations::JoinDependency.new(
- other.klass, other.table, joins_dependency, []
+ other.klass, other.table, joins_dependency, other.alias_tracker
)
relation.joins! rest
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index e76ad05b71..fb5beeffef 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1013,7 +1013,7 @@ module ActiveRecord
join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
join_dependency = ActiveRecord::Associations::JoinDependency.new(
- klass, table, association_joins, join_list
+ klass, table, association_joins, alias_tracker(join_list)
)
joins = join_dependency.join_constraints(stashed_association_joins, join_type)
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 3fa0ca8366..5b80f16a44 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -19,7 +19,7 @@ module ActiveRecord
spec "ridiculous://foo?encoding=utf8"
end
- assert_match "Could not load 'active_record/connection_adapters/ridiculous_adapter'", error.message
+ assert_match "Could not load the 'ridiculous' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", error.message
end
# The abstract adapter is used simply to bypass the bit of code that
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 55496147c1..2f6d708092 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -9,6 +9,7 @@ require "models/company"
require "models/tagging"
require "models/topic"
require "models/reply"
+require "models/rating"
require "models/entrant"
require "models/project"
require "models/developer"
@@ -244,6 +245,13 @@ class FinderTest < ActiveRecord::TestCase
assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists?
end
+ def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
+ assert_nothing_raised do
+ developer = developers(:david)
+ developer.ratings.includes(comment: :post).where(posts: { id: 1 }).exists?
+ end
+ end
+
def test_exists_with_empty_table_and_no_args_given
Topic.delete_all
assert_equal false, Topic.exists?