aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb278
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb257
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb43
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb42
-rwxr-xr-x[-rw-r--r--]activerecord/lib/active_record/connection_adapters/abstract_adapter.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb41
8 files changed, 475 insertions, 244 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
new file mode 100644
index 0000000000..54a39db1eb
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -0,0 +1,278 @@
+require 'monitor'
+require 'set'
+
+module ActiveRecord
+ # Raised when a connection could not be obtained within the connection
+ # acquisition timeout period.
+ class ConnectionTimeoutError < ConnectionNotEstablished
+ end
+
+ module ConnectionAdapters
+ # Connection pool base class for managing ActiveRecord database
+ # connections.
+ #
+ # Connections can be obtained and used from a connection pool in several
+ # ways:
+ #
+ # 1. Simply use ActiveRecord::Base.connection as with ActiveRecord 2.1 and
+ # earlier (pre-connection-pooling). Eventually, when you're done with
+ # the connection(s) and wish it to be returned to the pool, you call
+ # ActiveRecord::Base.clear_active_connections!. This will be the
+ # default behavior for ActiveRecord when used in conjunction with
+ # ActionPack's request handling cycle.
+ # 2. Manually check out a connection from the pool with
+ # ActiveRecord::Base.connection_pool.checkout. You are responsible for
+ # returning this connection to the pool when finished by calling
+ # ActiveRecord::Base.connection_pool.checkin(connection).
+ # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
+ # obtains a connection, yields it as the sole argument to the block,
+ # and returns it to the pool after the block completes.
+ #
+ # There are two connection-pooling-related options that you can add to
+ # your database connection configuration:
+ #
+ # * +pool+: number indicating size of connection pool (default 5)
+ # * +wait_timeout+: number of seconds to block and wait for a connection
+ # before giving up and raising a timeout error (default 5 seconds).
+ class ConnectionPool
+ attr_reader :spec
+
+ def initialize(spec)
+ @spec = spec
+ # The cache of reserved connections mapped to threads
+ @reserved_connections = {}
+ # The mutex used to synchronize pool access
+ @connection_mutex = Monitor.new
+ @queue = @connection_mutex.new_cond
+ # default 5 second timeout
+ @timeout = spec.config[:wait_timeout] || 5
+ # default max pool size to 5
+ @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
+ @connections = []
+ @checked_out = []
+ end
+
+ # Retrieve the connection associated with the current thread, or call
+ # #checkout to obtain one if necessary.
+ #
+ # #connection can be called any number of times; the connection is
+ # held in a hash keyed by the thread id.
+ def connection
+ if conn = @reserved_connections[current_connection_id]
+ conn
+ else
+ @reserved_connections[current_connection_id] = checkout
+ end
+ 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.
+ def release_connection
+ conn = @reserved_connections.delete(current_connection_id)
+ checkin conn if conn
+ end
+
+ # Reserve a connection, and yield it to a block. Ensure the connection is
+ # checked back in when finished.
+ def with_connection
+ conn = checkout
+ yield conn
+ ensure
+ checkin conn
+ end
+
+ # Returns true if a connection has already been opened.
+ def connected?
+ !@connections.empty?
+ end
+
+ # Disconnect all connections in the pool.
+ def disconnect!
+ @reserved_connections.each do |name,conn|
+ checkin conn
+ end
+ @reserved_connections = {}
+ @connections.each do |conn|
+ conn.disconnect!
+ end
+ @connections = []
+ end
+
+ # Clears the cache which maps classes
+ def clear_reloadable_connections!
+ @reserved_connections.each do |name, conn|
+ checkin conn
+ end
+ @reserved_connections = {}
+ @connections.each do |conn|
+ conn.disconnect! if conn.requires_reloading?
+ end
+ @connections = []
+ end
+
+ # Verify active connections and remove and disconnect connections
+ # associated with stale threads.
+ def verify_active_connections! #:nodoc:
+ clear_stale_cached_connections!
+ @connections.each do |connection|
+ connection.verify!
+ end
+ end
+
+ # Return any checked-out connections back to the pool by threads that
+ # are no longer alive.
+ def clear_stale_cached_connections!
+ remove_stale_cached_threads!(@reserved_connections) do |name, conn|
+ checkin conn
+ end
+ end
+
+ # Check-out a database connection from the pool.
+ def checkout
+ # Checkout an available connection
+ conn = @connection_mutex.synchronize do
+ if @checked_out.size < @connections.size
+ checkout_existing_connection
+ elsif @connections.size < @size
+ checkout_new_connection
+ end
+ end
+ return conn if conn
+
+ # No connections available; wait for one
+ @connection_mutex.synchronize do
+ if @queue.wait(@timeout)
+ checkout_existing_connection
+ else
+ raise ConnectionTimeoutError, "could not obtain a database connection within #{@timeout} seconds. The pool size is currently #{@size}, perhaps you need to increase it?"
+ end
+ end
+ end
+
+ # Check-in a database connection back into the pool.
+ def checkin(conn)
+ @connection_mutex.synchronize do
+ conn.run_callbacks :checkin
+ @checked_out.delete conn
+ @queue.signal
+ end
+ end
+
+ synchronize :clear_reloadable_connections!, :verify_active_connections!,
+ :connected?, :disconnect!, :with => :@connection_mutex
+
+ private
+ def new_connection
+ ActiveRecord::Base.send(spec.adapter_method, spec.config)
+ end
+
+ def current_connection_id #:nodoc:
+ Thread.current.object_id
+ end
+
+ # Remove stale threads from the cache.
+ def remove_stale_cached_threads!(cache, &block)
+ keys = Set.new(cache.keys)
+
+ Thread.list.each do |thread|
+ keys.delete(thread.object_id) if thread.alive?
+ end
+ keys.each do |key|
+ next unless cache.has_key?(key)
+ block.call(key, cache[key])
+ cache.delete(key)
+ end
+ end
+
+ def checkout_new_connection
+ c = new_connection
+ @connections << c
+ checkout_and_verify(c)
+ end
+
+ def checkout_existing_connection
+ c = (@connections - @checked_out).first
+ checkout_and_verify(c)
+ end
+
+ def checkout_and_verify(c)
+ c.verify!
+ c.run_callbacks :checkout
+ @checked_out << c
+ c
+ end
+ end
+
+ class ConnectionHandler
+ def initialize(pools = {})
+ @connection_pools = pools
+ end
+
+ def connection_pools
+ @connection_pools ||= {}
+ end
+
+ def establish_connection(name, spec)
+ @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
+ 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.
+ def clear_active_connections!
+ @connection_pools.each_value do |pool|
+ pool.release_connection
+ pool.clear_stale_cached_connections!
+ end
+ end
+
+ # Clears the cache which maps classes
+ def clear_reloadable_connections!
+ @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
+ end
+
+ def clear_all_connections!
+ @connection_pools.each_value {|pool| pool.disconnect! }
+ end
+
+ # Verify active connections.
+ def verify_active_connections! #:nodoc:
+ @connection_pools.each_value {|pool| pool.verify_active_connections! }
+ end
+
+ # Locate the connection of the nearest super class. This can be an
+ # active or defined connection: if it is the latter, it will be
+ # opened and set as the active connection for the class it was defined
+ # for (not necessarily the current class).
+ def retrieve_connection(klass) #:nodoc:
+ pool = retrieve_connection_pool(klass)
+ (pool && pool.connection) or raise ConnectionNotEstablished
+ end
+
+ # Returns true if a connection that's accessible to this class has
+ # already been opened.
+ def connected?(klass)
+ retrieve_connection_pool(klass).connected?
+ end
+
+ # Remove the connection for this class. This will close the active
+ # connection and the defined connection (if they exist). The result
+ # can be used as an argument for establish_connection, for easily
+ # re-establishing the connection.
+ def remove_connection(klass)
+ pool = @connection_pools[klass.name]
+ @connection_pools.delete_if { |key, value| value == pool }
+ pool.disconnect! if pool
+ pool.spec.config if pool
+ end
+
+ def retrieve_connection_pool(klass)
+ pool = @connection_pools[klass.name]
+ return pool if pool
+ return nil if ActiveRecord::Base == klass
+ retrieve_connection_pool klass.superclass
+ end
+ end
+ end
+end \ No newline at end of file
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 2a8807fb78..a968fc0fd3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -1,5 +1,3 @@
-require 'set'
-
module ActiveRecord
class Base
class ConnectionSpecification #:nodoc:
@@ -9,163 +7,9 @@ module ActiveRecord
end
end
- # Check for activity after at least +verification_timeout+ seconds.
- # Defaults to 0 (always check.)
- cattr_accessor :verification_timeout, :instance_writer => false
- @@verification_timeout = 0
-
- # The class -> [adapter_method, config] map
- @@defined_connections = {}
-
- # The class -> thread id -> adapter cache. (class -> adapter if not allow_concurrency)
- @@active_connections = {}
-
- class << self
- # Retrieve the connection cache.
- def thread_safe_active_connections #:nodoc:
- @@active_connections[Thread.current.object_id] ||= {}
- end
-
- def single_threaded_active_connections #:nodoc:
- @@active_connections
- end
-
- # pick up the right active_connection method from @@allow_concurrency
- if @@allow_concurrency
- alias_method :active_connections, :thread_safe_active_connections
- else
- alias_method :active_connections, :single_threaded_active_connections
- end
-
- # set concurrency support flag (not thread safe, like most of the methods in this file)
- def allow_concurrency=(threaded) #:nodoc:
- logger.debug "allow_concurrency=#{threaded}" if logger
- return if @@allow_concurrency == threaded
- clear_all_cached_connections!
- @@allow_concurrency = threaded
- method_prefix = threaded ? "thread_safe" : "single_threaded"
- sing = (class << self; self; end)
- [:active_connections, :scoped_methods].each do |method|
- sing.send(:alias_method, method, "#{method_prefix}_#{method}")
- end
- log_connections if logger
- end
-
- def active_connection_name #:nodoc:
- @active_connection_name ||=
- if active_connections[name] || @@defined_connections[name]
- name
- elsif self == ActiveRecord::Base
- nil
- else
- superclass.active_connection_name
- end
- end
-
- def clear_active_connection_name #:nodoc:
- @active_connection_name = nil
- subclasses.each { |klass| klass.clear_active_connection_name }
- end
-
- # Returns the connection currently associated with the class. This can
- # also be used to "borrow" the connection to do database work unrelated
- # to any of the specific Active Records.
- def connection
- if defined?(@active_connection_name) && (conn = active_connections[@active_connection_name])
- conn
- else
- # retrieve_connection sets the cache key.
- conn = retrieve_connection
- active_connections[@active_connection_name] = conn
- end
- end
-
- # Clears the cache which maps classes to connections.
- def clear_active_connections!
- clear_cache!(@@active_connections) do |name, conn|
- conn.disconnect!
- end
- end
-
- # Clears the cache which maps classes
- def clear_reloadable_connections!
- if @@allow_concurrency
- # With concurrent connections @@active_connections is
- # a hash keyed by thread id.
- @@active_connections.each do |thread_id, conns|
- conns.each do |name, conn|
- if conn.requires_reloading?
- conn.disconnect!
- @@active_connections[thread_id].delete(name)
- end
- end
- end
- else
- @@active_connections.each do |name, conn|
- if conn.requires_reloading?
- conn.disconnect!
- @@active_connections.delete(name)
- end
- end
- end
- end
-
- # Verify active connections.
- def verify_active_connections! #:nodoc:
- if @@allow_concurrency
- remove_stale_cached_threads!(@@active_connections) do |name, conn|
- conn.disconnect!
- end
- end
-
- active_connections.each_value do |connection|
- connection.verify!(@@verification_timeout)
- end
- end
-
- private
- def clear_cache!(cache, thread_id = nil, &block)
- if cache
- if @@allow_concurrency
- thread_id ||= Thread.current.object_id
- thread_cache, cache = cache, cache[thread_id]
- return unless cache
- end
-
- cache.each(&block) if block_given?
- cache.clear
- end
- ensure
- if thread_cache && @@allow_concurrency
- thread_cache.delete(thread_id)
- end
- end
-
- # Remove stale threads from the cache.
- def remove_stale_cached_threads!(cache, &block)
- stale = Set.new(cache.keys)
-
- Thread.list.each do |thread|
- stale.delete(thread.object_id) if thread.alive?
- end
-
- stale.each do |thread_id|
- clear_cache!(cache, thread_id, &block)
- end
- end
-
- def clear_all_cached_connections!
- if @@allow_concurrency
- @@active_connections.each_value do |connection_hash_for_thread|
- connection_hash_for_thread.each_value {|conn| conn.disconnect! }
- connection_hash_for_thread.clear
- end
- else
- @@active_connections.each_value {|conn| conn.disconnect! }
- end
- @@active_connections.clear
- end
- end
+ # The connection handler
+ cattr_accessor :connection_handler, :instance_writer => false
+ @@connection_handler = ConnectionAdapters::ConnectionHandler.new
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work that isn't
@@ -208,9 +52,7 @@ module ActiveRecord
raise AdapterNotSpecified unless defined? RAILS_ENV
establish_connection(RAILS_ENV)
when ConnectionSpecification
- clear_active_connection_name
- @active_connection_name = name
- @@defined_connections[name] = spec
+ @@connection_handler.establish_connection(name, spec)
when Symbol, String
if configuration = configurations[spec.to_s]
establish_connection(configuration)
@@ -243,67 +85,52 @@ module ActiveRecord
end
end
- # Locate the connection of the nearest super class. This can be an
- # active or defined connection: if it is the latter, it will be
- # opened and set as the active connection for the class it was defined
- # for (not necessarily the current class).
- def self.retrieve_connection #:nodoc:
- # Name is nil if establish_connection hasn't been called for
- # some class along the inheritance chain up to AR::Base yet.
- if name = active_connection_name
- if conn = active_connections[name]
- # Verify the connection.
- conn.verify!(@@verification_timeout)
- elsif spec = @@defined_connections[name]
- # Activate this connection specification.
- klass = name.constantize
- klass.connection = spec
- conn = active_connections[name]
- end
+ class << self
+ # Deprecated and no longer has any effect.
+ def allow_concurrency
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency has been deprecated and no longer has any effect. Please remove all references to allow_concurrency.")
end
- conn or raise ConnectionNotEstablished
- end
+ # Deprecated and no longer has any effect.
+ def allow_concurrency=(flag)
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency= has been deprecated and no longer has any effect. Please remove all references to allow_concurrency=.")
+ end
- # Returns true if a connection that's accessible to this class has already been opened.
- def self.connected?
- active_connections[active_connection_name] ? true : false
- end
+ # Deprecated and no longer has any effect.
+ def verification_timeout
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout has been deprecated and no longer has any effect. Please remove all references to verification_timeout.")
+ end
- # Remove the connection for this class. This will close the active
- # connection and the defined connection (if they exist). The result
- # can be used as an argument for establish_connection, for easily
- # re-establishing the connection.
- def self.remove_connection(klass=self)
- spec = @@defined_connections[klass.name]
- konn = active_connections[klass.name]
- @@defined_connections.delete_if { |key, value| value == spec }
- active_connections.delete_if { |key, value| value == konn }
- konn.disconnect! if konn
- spec.config if spec
- end
+ # Deprecated and no longer has any effect.
+ def verification_timeout=(flag)
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout= has been deprecated and no longer has any effect. Please remove all references to verification_timeout=.")
+ end
- # Set the connection for the class.
- def self.connection=(spec) #:nodoc:
- if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
- active_connections[name] = spec
- elsif spec.kind_of?(ConnectionSpecification)
- config = spec.config.reverse_merge(:allow_concurrency => @@allow_concurrency)
- self.connection = self.send(spec.adapter_method, config)
- elsif spec.nil?
- raise ConnectionNotEstablished
- else
- establish_connection spec
+ # Returns the connection currently associated with the class. This can
+ # also be used to "borrow" the connection to do database work unrelated
+ # to any of the specific Active Records.
+ def connection
+ retrieve_connection
end
- end
- # connection state logging
- def self.log_connections #:nodoc:
- if logger
- logger.info "Defined connections: #{@@defined_connections.inspect}"
- logger.info "Active connections: #{active_connections.inspect}"
- logger.info "Active connection name: #{@active_connection_name}"
+ def connection_pool
+ connection_handler.retrieve_connection_pool(self)
end
+
+ def retrieve_connection
+ connection_handler.retrieve_connection(self)
+ end
+
+ def connected?
+ connection_handler.connected?(self)
+ end
+
+ def remove_connection(klass = self)
+ connection_handler.remove_connection(klass)
+ end
+
+ delegate :clear_active_connections!, :clear_reloadable_connections!,
+ :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
end
end
end
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 aaf9e2e73f..8fc89de22b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -153,6 +153,10 @@ module ActiveRecord
"="
end
+ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
+ "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
+ end
+
protected
# Returns an array of record hashes with the column names as keys and
# column values as values.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 2afd6064ad..2fc50b9bfa 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -4,7 +4,6 @@ module ActiveRecord
class << self
def included(base)
base.class_eval do
- attr_accessor :query_cache_enabled
alias_method_chain :columns, :query_cache
alias_method_chain :select_all, :query_cache
end
@@ -16,7 +15,7 @@ module ActiveRecord
method_names.each do |method_name|
base.class_eval <<-end_code, __FILE__, __LINE__
def #{method_name}_with_query_dirty(*args)
- clear_query_cache if @query_cache_enabled
+ clear_query_cache if query_cache_enabled
#{method_name}_without_query_dirty(*args)
end
@@ -26,22 +25,38 @@ module ActiveRecord
end
end
+ def query_cache_enabled
+ Thread.current['query_cache_enabled']
+ end
+
+ def query_cache_enabled=(flag)
+ Thread.current['query_cache_enabled'] = flag
+ end
+
+ def query_cache
+ Thread.current['query_cache']
+ end
+
+ def query_cache=(cache)
+ Thread.current['query_cache'] = cache
+ end
+
# Enable the query cache within the block.
def cache
- old, @query_cache_enabled = @query_cache_enabled, true
- @query_cache ||= {}
+ old, self.query_cache_enabled = query_cache_enabled, true
+ self.query_cache ||= {}
yield
ensure
clear_query_cache
- @query_cache_enabled = old
+ self.query_cache_enabled = old
end
# Disable the query cache within the block.
def uncached
- old, @query_cache_enabled = @query_cache_enabled, false
+ old, self.query_cache_enabled = query_cache_enabled, false
yield
ensure
- @query_cache_enabled = old
+ self.query_cache_enabled = old
end
# Clears the query cache.
@@ -51,11 +66,11 @@ module ActiveRecord
# the same SQL query and repeatedly return the same result each time, silently
# undermining the randomness you were expecting.
def clear_query_cache
- @query_cache.clear if @query_cache
+ query_cache.clear if query_cache
end
def select_all_with_query_cache(*args)
- if @query_cache_enabled
+ if query_cache_enabled
cache_sql(args.first) { select_all_without_query_cache(*args) }
else
select_all_without_query_cache(*args)
@@ -63,8 +78,8 @@ module ActiveRecord
end
def columns_with_query_cache(*args)
- if @query_cache_enabled
- @query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
+ if query_cache_enabled
+ query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
else
columns_without_query_cache(*args)
end
@@ -73,11 +88,11 @@ module ActiveRecord
private
def cache_sql(sql)
result =
- if @query_cache.has_key?(sql)
+ if query_cache.has_key?(sql)
log_info(sql, "CACHE", 0.0)
- @query_cache[sql]
+ query_cache[sql]
else
- @query_cache[sql] = yield
+ query_cache[sql] = yield
end
if Array === result
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 75032efe57..58992f91da 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -40,6 +40,10 @@ module ActiveRecord
type == :integer || type == :float || type == :decimal
end
+ def has_default?
+ !default.nil?
+ end
+
# Returns the Ruby class that corresponds to the abstract data type.
def klass
case type
@@ -252,6 +256,10 @@ module ActiveRecord
class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
end
+ # Abstract representation of a column definition. Instances of this type
+ # are typically created by methods in TableDefinition, and added to the
+ # +columns+ attribute of said TableDefinition object, in order to be used
+ # for generating a number of table creation or table changing SQL statements.
class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
def sql_type
@@ -275,9 +283,29 @@ module ActiveRecord
end
end
- # Represents a SQL table in an abstract way.
- # Columns are stored as a ColumnDefinition in the +columns+ attribute.
+ # Represents the schema of an SQL table in an abstract way. This class
+ # provides methods for manipulating the schema representation.
+ #
+ # Inside migration files, the +t+ object in +create_table+ and
+ # +change_table+ is actually of this type:
+ #
+ # class SomeMigration < ActiveRecord::Migration
+ # def self.up
+ # create_table :foo do |t|
+ # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
+ # end
+ # end
+ #
+ # def self.down
+ # ...
+ # end
+ # end
+ #
+ # The table definitions
+ # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
class TableDefinition
+ # An array of ColumnDefinition objects, representing the column changes
+ # that have been defined.
attr_accessor :columns
def initialize(base)
@@ -321,6 +349,12 @@ module ActiveRecord
# * <tt>:scale</tt> -
# Specifies the scale for a <tt>:decimal</tt> column.
#
+ # For clarity's sake: the precision is the number of significant digits,
+ # while the scale is the number of digits that can be stored following
+ # the decimal point. For example, the number 123.45 has a precision of 5
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
+ # range from -999.99 to 999.99.
+ #
# Please be aware of different RDBMS implementations behavior with
# <tt>:decimal</tt> columns:
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
@@ -374,6 +408,10 @@ module ActiveRecord
# td.column(:huge_integer, :decimal, :precision => 30)
# # => huge_integer DECIMAL(30)
#
+ # # Defines a column with a database-specific type.
+ # td.column(:foo, 'polygon')
+ # # => foo polygon
+ #
# == Short-hand examples
#
# Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 6924bb7e6f..7c37916367 100644..100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -7,6 +7,7 @@ require 'active_record/connection_adapters/abstract/schema_definitions'
require 'active_record/connection_adapters/abstract/schema_statements'
require 'active_record/connection_adapters/abstract/database_statements'
require 'active_record/connection_adapters/abstract/quoting'
+require 'active_record/connection_adapters/abstract/connection_pool'
require 'active_record/connection_adapters/abstract/connection_specification'
require 'active_record/connection_adapters/abstract/query_cache'
@@ -24,6 +25,9 @@ module ActiveRecord
class AbstractAdapter
include Quoting, DatabaseStatements, SchemaStatements
include QueryCache
+ include ActiveSupport::Callbacks
+ define_callbacks :checkout, :checkin
+ checkout :reset!
@@row_even = true
def initialize(connection, logger = nil) #:nodoc:
@@ -102,20 +106,25 @@ module ActiveRecord
@active = false
end
+ # Reset the state of this connection, directing the DBMS to clear
+ # transactions and other connection-related server-side state. Usually a
+ # database-dependent operation; the default method simply executes a
+ # ROLLBACK and swallows any exceptions which is probably not enough to
+ # ensure the connection is clean.
+ def reset!
+ # this should be overridden by concrete adapters
+ end
+
# Returns true if its safe to reload the connection between requests for development mode.
# This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
def requires_reloading?
false
end
- # Lazily verify this connection, calling <tt>active?</tt> only if it hasn't
- # been called for +timeout+ seconds.
- def verify!(timeout)
- now = Time.now.to_i
- if (now - @last_verification) > timeout
- reconnect! unless active?
- @last_verification = now
- end
+ # Verify this connection by calling <tt>active?</tt> and reconnecting if
+ # the connection is no longer active.
+ def verify!(*ignored)
+ reconnect! unless active?
end
# Provides access to the underlying database connection. Useful for
@@ -138,10 +147,10 @@ module ActiveRecord
@open_transactions -= 1
end
- def log_info(sql, name, runtime)
+ def log_info(sql, name, seconds)
if @logger && @logger.debug?
- name = "#{name.nil? ? "SQL" : name} (#{sprintf("%f", runtime)})"
- @logger.debug format_log_entry(name, sql.squeeze(' '))
+ name = "#{name.nil? ? "SQL" : name} (#{sprintf("%.1f", seconds * 1000)}ms)"
+ @logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 204ebaa2e2..a26fd02b90 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -80,7 +80,7 @@ module ActiveRecord
def extract_default(default)
if type == :binary || type == :text
if default.blank?
- nil
+ return null ? nil : ''
else
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
end
@@ -91,6 +91,11 @@ module ActiveRecord
end
end
+ def has_default?
+ return false if type == :binary || type == :text #mysql forbids defaults on blob and text columns
+ super
+ end
+
private
def simplified_type(field_type)
return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
@@ -280,6 +285,14 @@ module ActiveRecord
@connection.close rescue nil
end
+ def reset!
+ if @connection.respond_to?(:change_user)
+ # See http://bugs.mysql.com/bug.php?id=33540 -- the workaround way to
+ # reset the connection is to change the user to the same user.
+ @connection.change_user(@config[:username], @config[:password], @config[:database])
+ configure_connection
+ end
+ end
# DATABASE STATEMENTS ======================================
@@ -515,6 +528,10 @@ module ActiveRecord
"= BINARY"
end
+ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
+ where_sql
+ end
+
private
def connect
@connection.reconnect = true if @connection.respond_to?(:reconnect=)
@@ -529,7 +546,11 @@ module ActiveRecord
end
@connection.real_connect(*@connection_options)
+ configure_connection
+ end
+ def configure_connection
+ encoding = @config[:encoding]
execute("SET NAMES '#{encoding}'") if encoding
# By default, MySQL 'where id is null' selects the last inserted id.
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 55c7da5b4f..bebab5d05d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -380,7 +380,7 @@ module ActiveRecord
# There are some incorrectly compiled postgres drivers out there
# that don't define PGconn.escape.
self.class.instance_eval do
- undef_method(:quote_string)
+ remove_method(:quote_string)
end
end
quote_string(s)
@@ -518,6 +518,45 @@ module ActiveRecord
execute "ROLLBACK"
end
+ # ruby-pg defines Ruby constants for transaction status,
+ # ruby-postgres does not.
+ PQTRANS_IDLE = defined?(PGconn::PQTRANS_IDLE) ? PGconn::PQTRANS_IDLE : 0
+
+ # Check whether a transaction is active.
+ def transaction_active?
+ @connection.transaction_status != PQTRANS_IDLE
+ end
+
+ # Wrap a block in a transaction. Returns result of block.
+ def transaction(start_db_transaction = true)
+ transaction_open = false
+ begin
+ if block_given?
+ if start_db_transaction
+ begin_db_transaction
+ transaction_open = true
+ end
+ yield
+ end
+ rescue Exception => database_transaction_rollback
+ if transaction_open && transaction_active?
+ transaction_open = false
+ rollback_db_transaction
+ end
+ raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
+ end
+ ensure
+ if transaction_open && transaction_active?
+ begin
+ commit_db_transaction
+ rescue Exception => database_transaction_rollback
+ rollback_db_transaction
+ raise
+ end
+ end
+ end
+
+
# SCHEMA STATEMENTS ========================================
def recreate_database(name) #:nodoc: