aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_part.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb1
-rw-r--r--activerecord/lib/active_record/base.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb48
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb4
-rw-r--r--activerecord/lib/active_record/fixtures.rb6
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb3
-rw-r--r--activerecord/lib/active_record/persistence.rb28
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/relation/batches.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb35
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb14
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
16 files changed, 133 insertions, 43 deletions
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index dfcb116392..1d2e8667e4 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -34,8 +34,7 @@ module ActiveRecord
when :destroy
target.destroy
when :nullify
- target.send("#{reflection.foreign_key}=", nil)
- target.save(:validations => false)
+ target.update_attribute(reflection.foreign_key, nil)
end
end
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
index 3279e56e7d..2b1d888a9a 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
@@ -22,7 +22,7 @@ module ActiveRecord
end
def aliased_table
- Arel::Nodes::TableAlias.new aliased_table_name, table
+ Arel::Nodes::TableAlias.new table, aliased_table_name
end
def ==(other)
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index fcdd31ddea..5f06452247 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -17,6 +17,11 @@ module ActiveRecord
@primary_key ||= reset_primary_key
end
+ # Returns a quoted version of the primary key name, used to construct SQL statements.
+ def quoted_primary_key
+ @quoted_primary_key ||= connection.quote_column_name(primary_key)
+ end
+
def reset_primary_key #:nodoc:
key = self == base_class ? get_primary_key(base_class.name) :
base_class.primary_key
@@ -43,7 +48,12 @@ module ActiveRecord
end
attr_accessor :original_primary_key
- attr_writer :primary_key
+
+ # Attribute writer for the primary key column
+ def primary_key=(value)
+ @quoted_primary_key = nil
+ @primary_key = value
+ end
# Sets the name of the primary key column to use to the given value,
# or (if the value is nil or false) to the value returned by the given
@@ -53,6 +63,7 @@ module ActiveRecord
# set_primary_key "sysid"
# end
def set_primary_key(value = nil, &block)
+ @quoted_primary_key = nil
@primary_key ||= ''
self.original_primary_key = @primary_key
value &&= value.to_s
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 3c4dab304e..7661676f8c 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -32,6 +32,7 @@ module ActiveRecord
@attributes[attr_name] = value
end
end
+ alias_method :raw_write_attribute, :write_attribute
private
# Handle *= for method_missing.
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index b778b0c0f0..fe81c7dc2f 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -437,9 +437,10 @@ module ActiveRecord #:nodoc:
self._attr_readonly = []
class << self # Class methods
- delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
+ delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
+ delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
- delegate :select, :group, :order, :except, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
+ delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
# Executes a custom SQL query against your database and returns all the results. The results will
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 4297c26413..b4db1eed18 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -151,6 +151,12 @@ module ActiveRecord
@reserved_connections[current_connection_id] ||= checkout
end
+ # Check to see if there is an active connection in this connection
+ # pool.
+ def active_connection?
+ @reserved_connections.key? current_connection_id
+ end
+
# Signal that the thread is finished with the current connection.
# #release_connection releases the connection-thread association
# and returns the connection to the pool.
@@ -346,6 +352,12 @@ module ActiveRecord
@connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
end
+ # Returns true if there are any active connections among the connection
+ # pools that the ConnectionHandler is managing.
+ def active_connections?
+ connection_pools.values.any? { |pool| pool.active_connection? }
+ end
+
# Returns any connections in use by the current thread back to the pool,
# and also returns connections to the pool cached by threads that are no
# longer alive.
@@ -405,18 +417,40 @@ module ActiveRecord
end
class ConnectionManagement
+ class Proxy # :nodoc:
+ attr_reader :body, :testing
+
+ def initialize(body, testing = false)
+ @body = body
+ @testing = testing
+ end
+
+ def each(&block)
+ body.each(&block)
+ end
+
+ def close
+ body.close if body.respond_to?(:close)
+
+ # Don't return connection (and perform implicit rollback) if
+ # this request is a part of integration test
+ ActiveRecord::Base.clear_active_connections! unless testing
+ end
+ end
+
def initialize(app)
@app = app
end
def call(env)
- @app.call(env)
- ensure
- # Don't return connection (and perform implicit rollback) if
- # this request is a part of integration test
- unless env.key?("rack.test")
- ActiveRecord::Base.clear_active_connections!
- end
+ testing = env.key?('rack.test')
+
+ status, headers, body = @app.call(env)
+
+ [status, headers, Proxy.new(body, testing)]
+ rescue
+ ActiveRecord::Base.clear_active_connections! unless testing
+ raise
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
index d88720c8bf..bcd3abc08d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -116,7 +116,11 @@ module ActiveRecord
connection_handler.remove_connection(klass)
end
- delegate :clear_active_connections!, :clear_reloadable_connections!,
+ def clear_active_connections!
+ connection_handler.clear_active_connections!
+ end
+
+ delegate :clear_reloadable_connections!,
:clear_all_connections!,:verify_active_connections!, :to => :connection_handler
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index ae61d6ce94..32229a8410 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -222,7 +222,7 @@ module ActiveRecord
# SCHEMA STATEMENTS ========================================
- def tables(name = nil) #:nodoc:
+ def tables(name = 'SCHEMA') #:nodoc:
sql = <<-SQL
SELECT name
FROM sqlite_master
@@ -350,7 +350,7 @@ module ActiveRecord
end
def table_structure(table_name)
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})").to_hash
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
structure
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index d523c643ba..0939ec2626 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -173,10 +173,10 @@ class FixturesFileNotFound < StandardError; end
# traversed in the database to create the fixture hash and/or instance variables. This is expensive for
# large sets of fixtured data.
#
-# = Dynamic fixtures with ERb
+# = Dynamic fixtures with ERB
#
# Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
-# mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
+# mix ERB in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
#
# <% for i in 1..1000 %>
# fix_<%= i %>:
@@ -186,7 +186,7 @@ class FixturesFileNotFound < StandardError; end
#
# This will create 1000 very simple YAML fixtures.
#
-# Using ERb, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
+# Using ERB, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
# This is however a feature to be used with some caution. The point of fixtures are that they're
# stable units of predictable sample data. If you feel that you need to inject dynamic values, then
# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index afadbf03ef..d31e321440 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -23,6 +23,9 @@ module ActiveRecord
return unless logger.debug?
payload = event.payload
+
+ return if 'SCHEMA' == payload[:name]
+
name = '%s (%.1fms)' % [payload[:name], event.duration]
sql = payload[:sql].squeeze(' ')
binds = nil
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 3377a5934b..a916c88348 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -104,17 +104,33 @@ module ActiveRecord
became
end
+ # Updates a single attribute and saves the record.
+ # This is especially useful for boolean flags on existing records. Also note that
+ #
+ # * Validation is skipped.
+ # * Callbacks are invoked.
+ # * updated_at/updated_on column is updated if that column is available.
+ # * Updates all the attributes that are dirty in this object.
+ #
+ def update_attribute(name, value)
+ name = name.to_s
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
+ send("#{name}=", value)
+ save(:validate => false)
+ end
+
# Updates a single attribute of an object, without calling save.
#
# * Validation is skipped.
# * Callbacks are skipped.
- # * updated_at/updated_on column is not updated in any case.
+ # * updated_at/updated_on column is not updated if that column is available.
#
def update_column(name, value)
name = name.to_s
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
- send("#{name}=", value)
- self.class.update_all({ name => value }, self.class.primary_key => id)
+ raise ActiveRecordError, "can not update on a new record object" unless persisted?
+ raw_write_attribute(name, value)
+ self.class.update_all({ name => value }, self.class.primary_key => id) == 1
end
# Updates the attributes of the model from the passed-in hash and saves the
@@ -154,7 +170,7 @@ module ActiveRecord
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def increment!(attribute, by = 1)
- increment(attribute, by).update_column(attribute, self[attribute])
+ increment(attribute, by).update_attribute(attribute, self[attribute])
end
# Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
@@ -171,7 +187,7 @@ module ActiveRecord
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def decrement!(attribute, by = 1)
- decrement(attribute, by).update_column(attribute, self[attribute])
+ decrement(attribute, by).update_attribute(attribute, self[attribute])
end
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
@@ -188,7 +204,7 @@ module ActiveRecord
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def toggle!(attribute)
- toggle(attribute).update_column(attribute, self[attribute])
+ toggle(attribute).update_attribute(attribute, self[attribute])
end
# Reloads the attributes of this object from the database.
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 8e545f9cad..896daf516e 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -12,7 +12,7 @@ module ActiveRecord
# These are explicitly delegated to improve performance (avoids method_missing)
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
- delegate :table_name, :primary_key, :to => :klass
+ delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass
attr_reader :table, :klass, :loaded
attr_accessor :extensions
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index bf5a60f458..d52b84179f 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -83,7 +83,7 @@ module ActiveRecord
private
def batch_order
- "#{table_name}.#{primary_key} ASC"
+ "#{quoted_table_name}.#{quoted_primary_key} ASC"
end
end
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index c1842b1a96..869eebfa34 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -196,24 +196,22 @@ module ActiveRecord
end
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
- column = aggregate_column(column_name)
-
# Postgresql doesn't like ORDER BY when there are no GROUP BY
relation = except(:order)
- select_value = operation_over_aggregate_column(column, operation, distinct)
- relation.select_values = [select_value]
+ if operation == "count" && (relation.limit_value || relation.offset_value)
+ # Shortcut when limit is zero.
+ return 0 if relation.limit_value == 0
- query_builder = relation.arel
+ query_builder = build_count_subquery(relation, column_name, distinct)
+ else
+ column = aggregate_column(column_name)
- if operation == "count"
- limit = relation.limit_value
- offset = relation.offset_value
+ select_value = operation_over_aggregate_column(column, operation, distinct)
- unless limit && offset
- query_builder.limit = nil
- query_builder.offset = nil
- end
+ relation.select_values = [select_value]
+
+ query_builder = relation.arel
end
type_cast_calculated_value(@klass.connection.select_value(query_builder.to_sql), column_for(column_name), operation)
@@ -312,5 +310,18 @@ module ActiveRecord
select if select !~ /(,|\*)/
end
end
+
+ def build_count_subquery(relation, column_name, distinct)
+ column_alias = Arel.sql('count_column')
+ subquery_alias = Arel.sql('subquery_for_count')
+
+ aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
+ relation.select_values = [aliased_column]
+ subquery = relation.arel.as(subquery_alias)
+
+ sm = Arel::SelectManager.new relation.engine
+ select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
+ sm.project(select_value).from(subquery)
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 563843f3cc..8fa315bdf3 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -183,7 +183,9 @@ module ActiveRecord
def exists?(id = nil)
id = id.id if ActiveRecord::Base === id
- relation = select("1").limit(1)
+ join_dependency = construct_join_dependency_for_association_find
+ relation = construct_relation_for_association_find(join_dependency)
+ relation = relation.except(:select).select("1").limit(1)
case id
when Array, Hash
@@ -192,14 +194,13 @@ module ActiveRecord
relation = relation.where(table[primary_key].eq(id)) if id
end
- relation.first ? true : false
+ connection.select_value(relation.to_sql) ? true : false
end
protected
def find_with_associations
- including = (@eager_load_values + @includes_values).uniq
- join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, [])
+ join_dependency = construct_join_dependency_for_association_find
relation = construct_relation_for_association_find(join_dependency)
rows = connection.select_all(relation.to_sql, 'SQL', relation.bind_values)
join_dependency.instantiate(rows)
@@ -207,6 +208,11 @@ module ActiveRecord
[]
end
+ def construct_join_dependency_for_association_find
+ including = (@eager_load_values + @includes_values).uniq
+ ActiveRecord::Associations::JoinDependency.new(@klass, including, [])
+ end
+
def construct_relation_for_association_calculations
including = (@eager_load_values + @includes_values).uniq
join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, arel.froms.first)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 9470e7c6c5..02b7056989 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -62,6 +62,10 @@ module ActiveRecord
relation
end
+ def reorder(*args)
+ except(:order).order(args)
+ end
+
def joins(*args)
return self if args.compact.blank?