aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb24
-rw-r--r--activerecord/lib/active_record/core.rb7
-rw-r--r--activerecord/lib/active_record/null_relation.rb53
-rw-r--r--activerecord/lib/active_record/relation.rb12
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/scoping/default.rb2
-rw-r--r--activerecord/lib/active_record/scoping/named.rb4
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_test.rb6
-rw-r--r--activerecord/test/cases/persistence_test.rb20
-rw-r--r--activerecord/test/cases/relations_test.rb33
-rw-r--r--activerecord/test/models/comment.rb2
15 files changed, 163 insertions, 27 deletions
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index fb0ca15c23..3c41a303d3 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -39,7 +39,7 @@ module ActiveRecord
# post.comments.aliased_table_name # => "comments"
#
def aliased_table_name
- reflection.klass.table_name
+ klass.table_name
end
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index d25a821688..fc3906d395 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -305,7 +305,7 @@ module ActiveRecord #:nodoc:
# * AssociationTypeMismatch - The object assigned to the association wasn't of the type
# specified in the association definition.
# * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
- # * ConnectionNotEstablished+ - No connection has been established. Use <tt>establish_connection</tt>
+ # * ConnectionNotEstablished - No connection has been established. Use <tt>establish_connection</tt>
# before querying.
# * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
# or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index e919068909..be712f2334 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -312,13 +312,27 @@ module ActiveRecord
# on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
# an UPDATE statement, so in the mysql adapters we redefine this to do that.
def join_to_update(update, select) #:nodoc:
- subselect = select.clone
- subselect.projections = [update.key]
+ key = update.key
+ subselect = subquery_for(key, select)
- update.where update.key.in(subselect)
+ update.where key.in(subselect)
+ end
+
+ def join_to_delete(delete, select, key) #:nodoc:
+ subselect = subquery_for(key, select)
+
+ delete.where key.in(subselect)
end
protected
+
+ # Return a subquery for the given key using the join information.
+ def subquery_for(key, select)
+ subselect = select.clone
+ subselect.projections = [key]
+ subselect
+ end
+
# Returns an array of record hashes with the column names as keys and
# column values as values.
def select(sql, name = nil, binds = [])
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 64f922b7ad..d0ea468430 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -294,19 +294,10 @@ module ActiveRecord
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
- # these, we must use a subquery. However, MySQL is too stupid to create a
- # temporary table for this automatically, so we have to give it some prompting
- # in the form of a subsubquery. Ugh!
+ # these, we must use a subquery.
def join_to_update(update, select) #:nodoc:
if select.limit || select.offset || select.orders.any?
- subsubselect = select.clone
- subsubselect.projections = [update.key]
-
- subselect = Arel::SelectManager.new(select.engine)
- subselect.project Arel.sql(update.key.name)
- subselect.from subsubselect.as('__active_record_temp')
-
- update.where update.key.in(subselect)
+ super
else
update.table select.source
update.wheres = select.constraints
@@ -558,6 +549,17 @@ module ActiveRecord
protected
+ # MySQL is too stupid to create a temporary table for use subquery, so we have
+ # to give it some prompting in the form of a subsubquery. Ugh!
+ def subquery_for(key, select)
+ subsubselect = select.clone
+ subsubselect.projections = [key]
+
+ subselect = Arel::SelectManager.new(select.engine)
+ subselect.project Arel.sql(key.name)
+ subselect.from subsubselect.as('__active_record_temp')
+ end
+
def add_index_length(option_strings, column_names, options = {})
if options.is_a?(Hash) && length = options[:length]
case length
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 76c424e8b4..eb8f4ad669 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -137,12 +137,12 @@ module ActiveRecord
private
def relation #:nodoc:
- @relation ||= Relation.new(self, arel_table)
+ relation = Relation.new(self, arel_table)
if finder_needs_type_condition?
- @relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
+ relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
else
- @relation
+ relation
end
end
end
@@ -351,7 +351,6 @@ module ActiveRecord
@attributes[pk] = nil unless @attributes.key?(pk)
- @relation = nil
@aggregation_cache = {}
@association_cache = {}
@attributes_cache = {}
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index 60c37ac2b7..c2d3eeb8ce 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -6,5 +6,58 @@ module ActiveRecord
def exec_queries
@records = []
end
+
+ def pluck(column_name)
+ []
+ end
+
+ def delete_all(conditions = nil)
+ 0
+ end
+
+ def update_all(updates, conditions = nil, options = {})
+ 0
+ end
+
+ def delete(id_or_array)
+ 0
+ end
+
+ def size
+ 0
+ end
+
+ def empty?
+ true
+ end
+
+ def any?
+ false
+ end
+
+ def many?
+ false
+ end
+
+ def to_sql
+ @to_sql ||= ""
+ end
+
+ def where_values_hash
+ {}
+ end
+
+ def count
+ 0
+ end
+
+ def calculate(operation, column_name, options = {})
+ nil
+ end
+
+ def exists?(id = false)
+ false
+ end
+
end
end \ No newline at end of file
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index b125449127..b007b8c168 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -400,8 +400,16 @@ module ActiveRecord
if conditions
where(conditions).delete_all
else
- statement = arel.compile_delete
- affected = @klass.connection.delete(statement, 'SQL', bind_values)
+ stmt = Arel::DeleteManager.new(arel.engine)
+ stmt.from(table)
+
+ if joins_values.any?
+ @klass.connection.join_to_delete(stmt, arel, table[primary_key])
+ else
+ stmt.wheres = arel.constraints
+ end
+
+ affected = @klass.connection.delete(stmt, 'SQL', bind_values)
reset
affected
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 95fd33c1d1..7cbe2db408 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -55,7 +55,7 @@ module ActiveRecord
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
-# It's strongly recommended to check this file into your version control system.
+# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(#{define_params}) do
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index b0609a8c08..45c34005c3 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -31,7 +31,7 @@ module ActiveRecord
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
#
- # It is recommended to use the block form of unscoped because chaining
+ # It is recommended that you use the block form of unscoped because chaining
# unscoped with <tt>scope</tt> does not work. Assuming that
# <tt>published</tt> is a <tt>scope</tt>, the following two statements
# are equal: the default_scope is applied on both.
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 077e2d067e..b43e08157a 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -35,7 +35,7 @@ module ActiveRecord
if current_scope
current_scope.clone
else
- scope = relation.clone
+ scope = relation
scope.default_scoped = true
scope
end
@@ -49,7 +49,7 @@ module ActiveRecord
if current_scope
current_scope.scope_for_create
else
- scope = relation.clone
+ scope = relation
scope.default_scoped = true
scope.scope_for_create
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 1160d236c9..9dd041af81 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -168,6 +168,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable = Member.new :name => "Bert"
assert_equal Member, sponsor.association(:sponsorable).send(:klass)
+ assert_equal "members", sponsor.association(:sponsorable).aliased_table_name
end
def test_with_polymorphic_and_condition
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index efdb7cbb36..f7f44208b3 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1174,6 +1174,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal Comment.find(1), Comment.preload(:post => :comments).scoping { Comment.find(1) }
end
+ test "circular preload does not modify unscoped" do
+ expected = FirstPost.unscoped.find(2)
+ FirstPost.preload(:comments => :first_post).find(1)
+ assert_equal expected, FirstPost.unscoped.find(2)
+ end
+
test "preload ignores the scoping" do
assert_equal(
Comment.find(1).post,
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index adfd8e83a1..bb60aa0a66 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -13,12 +13,14 @@ require 'models/warehouse_thing'
require 'models/parrot'
require 'models/minivan'
require 'models/person'
+require 'models/pet'
+require 'models/toy'
require 'rexml/document'
require 'active_support/core_ext/exception'
class PersistencesTest < ActiveRecord::TestCase
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans, :pets, :toys
# Oracle UPDATE does not support ORDER BY
unless current_adapter?(:OracleAdapter)
@@ -76,6 +78,22 @@ class PersistencesTest < ActiveRecord::TestCase
assert_equal Topic.count, Topic.delete_all
end
+ def test_delete_all_with_joins_and_where_part_is_hash
+ where_args = {:toys => {:name => 'Bone'}}
+ count = Pet.joins(:toys).where(where_args).count
+
+ assert_equal count, 1
+ assert_equal count, Pet.joins(:toys).where(where_args).delete_all
+ end
+
+ def test_delete_all_with_joins_and_where_part_is_not_hash
+ where_args = ['toys.name = ?', 'Bone']
+ count = Pet.joins(:toys).where(where_args).count
+
+ assert_equal count, 1
+ assert_equal count, Pet.joins(:toys).where(where_args).delete_all
+ end
+
def test_update_by_condition
Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
assert_equal "Have a nice day", Topic.find(1).content
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 25eb7c1672..5e938968a3 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -219,6 +219,7 @@ class RelationTest < ActiveRecord::TestCase
assert_no_queries do
assert_equal [], Developer.none
assert_equal [], Developer.scoped.none
+ assert Developer.none.is_a?(ActiveRecord::NullRelation)
end
end
@@ -228,6 +229,38 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_none_chained_to_methods_firing_queries_straight_to_db
+ assert_no_queries do
+ assert_equal [], Developer.none.pluck(:id) # => uses select_all
+ assert_equal 0, Developer.none.delete_all
+ assert_equal 0, Developer.none.update_all(:name => 'David')
+ assert_equal 0, Developer.none.delete(1)
+ assert_equal false, Developer.none.exists?(1)
+ end
+ end
+
+ def test_null_relation_content_size_methods
+ assert_no_queries do
+ assert_equal 0, Developer.none.size
+ assert_equal 0, Developer.none.count
+ assert_equal true, Developer.none.empty?
+ assert_equal false, Developer.none.any?
+ assert_equal false, Developer.none.many?
+ end
+ end
+
+ def test_null_relation_calculations_methods
+ assert_no_queries do
+ assert_equal 0, Developer.none.count
+ assert_equal nil, Developer.none.calculate(:average, 'salary')
+ end
+ end
+
+ def test_null_relation_metadata_methods
+ assert_equal "", Developer.none.to_sql
+ assert_equal({}, Developer.none.where_values_hash)
+ end
+
def test_joins_with_nil_argument
assert_nothing_raised { DependentFirm.joins(nil).first }
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index 00f5a74070..cf73c28ded 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -9,6 +9,8 @@ class Comment < ActiveRecord::Base
belongs_to :post, :counter_cache => true
has_many :ratings
+ belongs_to :first_post, :foreign_key => :post_id
+
has_many :children, :class_name => 'Comment', :foreign_key => :parent_id
belongs_to :parent, :class_name => 'Comment', :counter_cache => :children_count