diff options
Diffstat (limited to 'activerecord')
35 files changed, 395 insertions, 539 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index a79f4df570..c50229e779 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -65,8 +65,42 @@ *Aaron Christy* +## Rails 3.1.3 (unreleased) ## + +* Perf fix: If we're deleting all records in an association, don't add a IN(..) clause + to the query. *GH 3672* + + *Jon Leighton* + +* Fix bug with referencing other mysql databases in set_table_name. *GH 3690* + +* Fix performance bug with mysql databases on a server with lots of other databses. *GH 3678* + + *Christos Zisopoulos and Kenny J* + ## Rails 3.1.2 (unreleased) ## +* Fix bug with PostgreSQLAdapter#indexes. When the search path has multiple schemas, spaces + were not being stripped from the schema names after the first. + + *Sean Kirby* + +* Preserve SELECT columns on the COUNT for finder_sql when possible. *GH 3503* + + *Justin Mazzi* + +* Reset prepared statement cache when schema changes impact statement results. *GH 3335* + + *Aaron Patterson* + +* Postgres: Do not attempt to deallocate a statement if the connection is no longer active. + + *Ian Leitch* + +* Prevent QueryCache leaking database connections. *GH 3243* + + *Mark J. Titorenko* + * Fix bug where building the conditions of a nested through association could potentially modify the conditions of the through and/or source association. If you have experienced bugs with conditions appearing in the wrong queries when using nested through associations, diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index b4622005b4..52a3d4a501 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -21,6 +21,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('arel', '~> 2.2.1') + s.add_dependency('arel', '~> 3.0.0.pre') s.add_dependency('tzinfo', '~> 0.3.29') end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 50ee60284c..3353cdf1ef 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -89,8 +89,12 @@ module ActiveRecord records.each { |r| r.destroy } update_counter(-records.length) unless inverse_updates_counter_cache? else - keys = records.map { |r| r[reflection.association_primary_key] } - scope = scoped.where(reflection.association_primary_key => keys) + scope = scoped + + unless records == load_target + keys = records.map { |r| r[reflection.association_primary_key] } + scope = scoped.where(reflection.association_primary_key => keys) + end if method == :delete_all update_counter(-scope.delete_all) diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 4174e4da09..4a5afcd585 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -77,7 +77,7 @@ module ActiveRecord # # The second, slower, branch is necessary to support instances where the database # returns columns with extra stuff in (like 'my_column(omg)'). - if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP + if method_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ def _#{method_name} #{access_code} diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index e9cdb130db..eb585ee906 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -10,7 +10,7 @@ module ActiveRecord module ClassMethods protected def define_method_attribute=(attr_name) - if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP + if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__) else generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value| diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3558ae3545..7ba67b8540 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -710,21 +710,21 @@ module ActiveRecord #:nodoc: # Returns an array of column objects for the table associated with this class. def columns if defined?(@primary_key) - connection_pool.primary_keys[table_name] ||= primary_key + connection.schema_cache.primary_keys[table_name] ||= primary_key end - connection_pool.columns[table_name] + connection.schema_cache.columns[table_name] end # Returns a hash of column objects for the table associated with this class. def columns_hash - connection_pool.columns_hash[table_name] + connection.schema_cache.columns_hash[table_name] end # Returns a hash where the keys are column names and the values are # default values when instantiating the AR object for this table. def column_defaults - connection_pool.column_defaults[table_name] + connection.schema_cache.column_defaults[table_name] end # Returns an array of column names as strings. @@ -781,14 +781,14 @@ module ActiveRecord #:nodoc: def reset_column_information connection.clear_cache! undefine_attribute_methods - connection_pool.clear_table_cache!(table_name) if table_exists? + connection.schema_cache.clear_table_cache!(table_name) if table_exists? @column_names = @content_columns = @dynamic_methods_hash = @inheritance_column = nil @arel_engine = @relation = nil end def clear_cache! # :nodoc: - connection_pool.clear_cache! + connection.schema_cache.clear! end def attribute_method?(attribute) @@ -1356,9 +1356,9 @@ MSG return nil if condition.blank? case condition - when Array; sanitize_sql_array(condition) - when Hash; sanitize_sql_hash_for_conditions(condition, table_name) - else condition + when Array; sanitize_sql_array(condition) + when Hash; sanitize_sql_hash_for_conditions(condition, table_name) + else condition end end alias_method :sanitize_sql, :sanitize_sql_for_conditions 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 92dfb844db..e32154780a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -2,6 +2,7 @@ require 'thread' require 'monitor' require 'set' require 'active_support/core_ext/module/synchronization' +require 'active_support/core_ext/module/deprecation' module ActiveRecord # Raised when a connection could not be obtained within the connection @@ -59,8 +60,6 @@ module ActiveRecord class ConnectionPool attr_accessor :automatic_reconnect attr_reader :spec, :connections - attr_reader :columns, :columns_hash, :primary_keys, :tables - attr_reader :column_defaults # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification # object which describes database connection information (e.g. adapter, @@ -85,72 +84,7 @@ module ActiveRecord @connections = [] @checked_out = [] @automatic_reconnect = true - @tables = {} @visitor = nil - - @columns = Hash.new do |h, table_name| - h[table_name] = with_connection do |conn| - - # Fetch a list of columns - conn.columns(table_name, "#{table_name} Columns").tap do |columns| - - # set primary key information - columns.each do |column| - column.primary = column.name == primary_keys[table_name] - end - end - end - end - - @columns_hash = Hash.new do |h, table_name| - h[table_name] = Hash[columns[table_name].map { |col| - [col.name, col] - }] - end - - @column_defaults = Hash.new do |h, table_name| - h[table_name] = Hash[columns[table_name].map { |col| - [col.name, col.default] - }] - end - - @primary_keys = Hash.new do |h, table_name| - h[table_name] = with_connection do |conn| - table_exists?(table_name) ? conn.primary_key(table_name) : 'id' - end - end - end - - # A cached lookup for table existence. - def table_exists?(name) - return @tables[name] if @tables.key? name - - with_connection do |conn| - conn.tables.each { |table| @tables[table] = true } - @tables[name] = !@tables.key?(name) && conn.table_exists?(name) - end - - @tables[name] - end - - # Clears out internal caches: - # - # * columns - # * columns_hash - # * tables - def clear_cache! - @columns.clear - @columns_hash.clear - @column_defaults.clear - @tables.clear - end - - # Clear out internal caches for table with +table_name+. - def clear_table_cache!(table_name) - @columns.delete table_name - @columns_hash.delete table_name - @column_defaults.delete table_name - @primary_keys.delete table_name end # Retrieve the connection associated with the current thread, or call @@ -227,6 +161,34 @@ module ActiveRecord end end + def columns + with_connection do |c| + c.schema_cache.columns + end + end + deprecate :columns + + def columns_hash + with_connection do |c| + c.schema_cache.columns_hash + end + end + deprecate :columns_hash + + def primary_keys + with_connection do |c| + c.schema_cache.primary_keys + end + end + deprecate :primary_keys + + def clear_cache! + with_connection do |c| + c.schema_cache.clear! + end + end + deprecate :clear_cache! + # Return any checked-out connections back to the pool by threads that # are no longer alive. def clear_stale_cached_connections! @@ -301,16 +263,7 @@ module ActiveRecord private def new_connection - connection = ActiveRecord::Base.send(spec.adapter_method, spec.config) - - # TODO: This is a bit icky, and in the long term we may want to change the method - # signature for connections. Also, if we switch to have one visitor per - # connection (and therefore per thread), we can get rid of the thread-local - # variable in Arel::Visitors::ToSql. - @visitor ||= connection.class.visitor_for(self) - connection.visitor = @visitor - - connection + ActiveRecord::Base.send(spec.adapter_method, spec.config) end def current_connection_id #:nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 11da84e245..faa42e2d19 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/array/wrap' +require 'active_support/deprecation/reporting' module ActiveRecord module ConnectionAdapters # :nodoc: @@ -154,17 +155,11 @@ module ActiveRecord # ) # # See also TableDefinition#column for details on how to create columns. - def create_table(table_name, options = {}, &blk) + def create_table(table_name, options = {}) td = table_definition td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false - if block_given? - if blk.arity == 1 - yield td - else - td.instance_eval(&blk) - end - end + yield td if block_given? if options[:force] && table_exists?(table_name) drop_table(table_name) @@ -241,19 +236,14 @@ module ActiveRecord # # See also Table for details on # all of the various column transformation - def change_table(table_name, options = {}, &blk) - bulk_change = supports_bulk_alter? && options[:bulk] - recorder = bulk_change ? ActiveRecord::Migration::CommandRecorder.new(self) : self - table = Table.new(table_name, recorder) - - if block_given? - if blk.arity == 1 - yield table - else - table.instance_eval(&blk) - end + def change_table(table_name, options = {}) + if supports_bulk_alter? && options[:bulk] + recorder = ActiveRecord::Migration::CommandRecorder.new(self) + yield Table.new(table_name, recorder) + bulk_change_table(table_name, recorder.commands) + else + yield Table.new(table_name, self) end - bulk_change_table(table_name, recorder.commands) if bulk_change end # Renames a table. @@ -445,6 +435,7 @@ module ActiveRecord si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix if table_exists?(si_table) + ActiveRecord::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table" old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i assume_migrated_upto_version(old_version) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c47bcfc406..75e568b557 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -3,6 +3,7 @@ require 'bigdecimal' require 'bigdecimal/util' require 'active_support/core_ext/benchmark' require 'active_support/deprecation' +require 'active_record/connection_adapters/schema_cache' module ActiveRecord module ConnectionAdapters # :nodoc: @@ -51,6 +52,7 @@ module ActiveRecord define_callbacks :checkout, :checkin attr_accessor :visitor + attr_reader :schema_cache def initialize(connection, logger = nil) #:nodoc: @active = nil @@ -60,24 +62,7 @@ module ActiveRecord @open_transactions = 0 @instrumenter = ActiveSupport::Notifications.instrumenter @visitor = nil - end - - # Returns a visitor instance for this adaptor, which conforms to the Arel::ToSql interface - def self.visitor_for(pool) # :nodoc: - adapter = pool.spec.config[:adapter] - - if Arel::Visitors::VISITORS[adapter] - ActiveSupport::Deprecation.warn( - "Arel::Visitors::VISITORS is deprecated and will be removed. Database adapters " \ - "should define a visitor_for method which returns the appropriate visitor for " \ - "the database. For example, MysqlAdapter.visitor_for(pool) returns " \ - "Arel::Visitors::MySQL.new(pool)." - ) - - Arel::Visitors::VISITORS[adapter].new(pool) - else - Arel::Visitors::ToSql.new(pool) - end + @schema_cache = SchemaCache.new self end # Returns the human-readable name of the adapter. Use mixed case - one 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 baf4c043c4..f143fd348e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -127,10 +127,7 @@ module ActiveRecord super(connection, logger) @connection_options, @config = connection_options, config @quoted_column_names, @quoted_table_names = {}, {} - end - - def self.visitor_for(pool) # :nodoc: - Arel::Visitors::MySQL.new(pool) + @visitor = Arel::Visitors::MySQL.new self end def adapter_name #:nodoc: @@ -389,11 +386,11 @@ module ActiveRecord sql = "SHOW TABLES" end - select_all(sql).map do |table| + select_all(sql).map { |table| table.delete('Table_type') sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}" exec_without_stmt(sql).first['Create Table'] + ";\n\n" - end.join("") + }.join end # Drops the database specified on the +name+ attribute @@ -576,17 +573,8 @@ module ActiveRecord # Returns a table's primary key and belonging sequence. def pk_and_sequence_for(table) - sql = <<-SQL - SELECT t.constraint_type, k.column_name - FROM information_schema.table_constraints t - JOIN information_schema.key_column_usage k - USING (constraint_name, table_schema, table_name) - WHERE t.table_schema = DATABASE() - AND t.table_name = '#{table}' - SQL - - execute_and_free(sql, 'SCHEMA') do |result| - keys = each_hash(result).select { |row| row[:constraint_type] == 'PRIMARY KEY' }.map { |row| row[:column_name] } + execute_and_free("SHOW INDEX FROM #{quote_table_name(table)} WHERE Key_name = 'PRIMARY'", 'SCHEMA') do |result| + keys = each_hash(result).map { |row| row[:Column_name] } keys.length == 1 ? [keys.first, nil] : nil end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index b7918c7f07..2f01fbb829 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -307,6 +307,7 @@ module ActiveRecord def initialize(connection, logger, connection_parameters, config) super(connection, logger) @connection_parameters, @config = connection_parameters, config + @visitor = Arel::Visitors::PostgreSQL.new self # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil @@ -323,10 +324,6 @@ module ActiveRecord @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"] end - def self.visitor_for(pool) # :nodoc: - Arel::Visitors::PostgreSQL.new(pool) - end - # Clears the prepared statements cache. def clear_cache! @statements.clear diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb new file mode 100644 index 0000000000..b14b37ce89 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -0,0 +1,72 @@ +module ActiveRecord + module ConnectionAdapters + class SchemaCache + attr_reader :columns, :columns_hash, :primary_keys, :tables + attr_reader :column_defaults + attr_reader :connection + + def initialize(conn) + @connection = conn + @tables = {} + + @columns = Hash.new do |h, table_name| + h[table_name] = + # Fetch a list of columns + conn.columns(table_name, "#{table_name} Columns").tap do |cs| + # set primary key information + cs.each do |column| + column.primary = column.name == primary_keys[table_name] + end + end + end + + @columns_hash = Hash.new do |h, table_name| + h[table_name] = Hash[columns[table_name].map { |col| + [col.name, col] + }] + end + + @column_defaults = Hash.new do |h, table_name| + h[table_name] = Hash[columns[table_name].map { |col| + [col.name, col.default] + }] + end + + @primary_keys = Hash.new do |h, table_name| + h[table_name] = table_exists?(table_name) ? + conn.primary_key(table_name) : 'id' + end + end + + # A cached lookup for table existence. + def table_exists?(name) + return @tables[name] if @tables.key? name + + connection.tables.each { |table| @tables[table] = true } + @tables[name] = connection.table_exists?(name) if !@tables.key?(name) + + @tables[name] + end + + # Clears out internal caches: + # + # * columns + # * columns_hash + # * tables + def clear! + @columns.clear + @columns_hash.clear + @column_defaults.clear + @tables.clear + end + + # Clear out internal caches for table with +table_name+. + def clear_table_cache!(table_name) + @columns.delete table_name + @columns_hash.delete table_name + @column_defaults.delete table_name + @primary_keys.delete table_name + end + end + 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 35df0a1542..c11f82a33f 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -89,10 +89,7 @@ module ActiveRecord @statements = StatementPool.new(@connection, config.fetch(:statement_limit) { 1000 }) @config = config - end - - def self.visitor_for(pool) # :nodoc: - Arel::Visitors::SQLite.new(pool) + @visitor = Arel::Visitors::SQLite.new self end def adapter_name #:nodoc: @@ -480,30 +477,28 @@ module ActiveRecord drop_table(from) end - def copy_table(from, to, options = {}, &block) #:nodoc: - from_columns, from_primary_key = columns(from), primary_key(from) - options = options.merge(:id => (!from_columns.detect {|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) - table_definition = nil + def copy_table(from, to, options = {}) #:nodoc: + options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) create_table(to, options) do |definition| - table_definition = definition - from_columns.each do |column| + @definition = definition + columns(from).each do |column| column_name = options[:rename] ? (options[:rename][column.name] || options[:rename][column.name.to_sym] || column.name) : column.name - table_definition.column(column_name, column.type, + @definition.column(column_name, column.type, :limit => column.limit, :default => column.default, :precision => column.precision, :scale => column.scale, :null => column.null) end - table_definition.primary_key from_primary_key if from_primary_key - table_definition.instance_eval(&block) if block + @definition.primary_key(primary_key(from)) if primary_key(from) + yield @definition if block_given? end copy_table_indexes(from, to, options[:rename] || {}) copy_table_contents(from, to, - table_definition.columns.map {|column| column.name}, + @definition.columns.map {|column| column.name}, options[:rename] || {}) end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 5e65e46a7d..f047a1d9fa 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -114,6 +114,7 @@ module ActiveRecord became.instance_variable_set("@attributes_cache", @attributes_cache) became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) + became.instance_variable_set("@errors", errors) became.type = klass.name unless self.class.descends_from_active_record? became end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 44848b3391..abd71793fd 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -1,8 +1,8 @@ require 'active_support/core_ext/object/inclusion' +require 'active_record' db_namespace = namespace :db do task :load_config => :rails_env do - require 'active_record' ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a @@ -150,7 +150,16 @@ db_namespace = namespace :db do task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) - db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['_dump'].invoke + end + + task :_dump do + case ActiveRecord::Base.schema_format + when :ruby then db_namespace["schema:dump"].invoke + when :sql then db_namespace["structure:dump"].invoke + else + raise "unknown schema format #{ActiveRecord::Base.schema_format}" + end end namespace :migrate do @@ -173,7 +182,7 @@ db_namespace = namespace :db do version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil raise 'VERSION is required' unless version ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['_dump'].invoke end # desc 'Runs the "down" for a given migration VERSION.' @@ -181,7 +190,7 @@ db_namespace = namespace :db do version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil raise 'VERSION is required' unless version ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['_dump'].invoke end desc 'Display status of migrations' @@ -221,18 +230,21 @@ db_namespace = namespace :db do task :rollback => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['_dump'].invoke end # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' task :forward => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['_dump'].invoke end # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' - task :reset => [ 'db:drop', 'db:setup' ] + task :reset => :environment do + db_namespace["drop"].invoke + db_namespace["setup"].invoke + end # desc "Retrieves the charset for the current environment's database" task :charset => :environment do @@ -271,24 +283,23 @@ db_namespace = namespace :db do # desc "Raises an error if there are pending migrations" task :abort_if_pending_migrations => :environment do - if defined? ActiveRecord - pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations + pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations - if pending_migrations.any? - puts "You have #{pending_migrations.size} pending migrations:" - pending_migrations.each do |pending_migration| - puts ' %4d %s' % [pending_migration.version, pending_migration.name] - end - abort %{Run `rake db:migrate` to update your database then try again.} + if pending_migrations.any? + puts "You have #{pending_migrations.size} pending migrations:" + pending_migrations.each do |pending_migration| + puts ' %4d %s' % [pending_migration.version, pending_migration.name] end + abort %{Run `rake db:migrate` to update your database then try again.} end end desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)' - task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] + task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed] desc 'Load the seed data from db/seeds.rb' - task :seed => 'db:abort_if_pending_migrations' do + task :seed do + db_namespace['abort_if_pending_migrations'].invoke Rails.application.load_seed end @@ -351,6 +362,10 @@ db_namespace = namespace :db do abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded} end end + + task :load_if_ruby => 'db:create' do + db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby + end end namespace :structure do @@ -360,81 +375,94 @@ db_namespace = namespace :db do case abcs[Rails.env]['adapter'] when /mysql/, 'oci', 'oracle' ActiveRecord::Base.establish_connection(abcs[Rails.env]) - File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } + File.open("#{Rails.root}/db/structure.sql", "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump } when /postgresql/ - ENV['PGHOST'] = abcs[Rails.env]['host'] if abcs[Rails.env]['host'] - ENV['PGPORT'] = abcs[Rails.env]["port"].to_s if abcs[Rails.env]['port'] - ENV['PGPASSWORD'] = abcs[Rails.env]['password'].to_s if abcs[Rails.env]['password'] + set_psql_env(abcs[Rails.env]) search_path = abcs[Rails.env]['schema_search_path'] unless search_path.blank? search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ") end - `pg_dump -i -U "#{abcs[Rails.env]['username']}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]['database']}` + `pg_dump -i -s -x -O -f db/structure.sql #{search_path} #{abcs[Rails.env]['database']}` raise 'Error dumping database' if $?.exitstatus == 1 when /sqlite/ - dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile'] - `sqlite3 #{dbfile} .schema > db/#{Rails.env}_structure.sql` + dbfile = abcs[Rails.env]['database'] + `sqlite3 #{dbfile} .schema > db/structure.sql` when 'sqlserver' - `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\#{Rails.env}_structure.sql -A -U` + `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\structure.sql -A -U` when "firebird" set_firebird_env(abcs[Rails.env]) db_string = firebird_db_string(abcs[Rails.env]) - sh "isql -a #{db_string} > #{Rails.root}/db/#{Rails.env}_structure.sql" + sh "isql -a #{db_string} > #{Rails.root}/db/structure.sql" else raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'" end if ActiveRecord::Base.connection.supports_migrations? - File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } + File.open("#{Rails.root}/db/structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } end end - end - namespace :test do - # desc "Recreate the test database from the current schema.rb" - task :load => 'db:test:purge' do - ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) - ActiveRecord::Schema.verbose = false - db_namespace['schema:load'].invoke - end - - # desc "Recreate the test database from the current environment's database schema" - task :clone => %w(db:schema:dump db:test:load) + # desc "Recreate the databases from the structure.sql file" + task :load => [:environment, :load_config] do + env = ENV['RAILS_ENV'] || 'test' - # desc "Recreate the test databases from the development structure" - task :clone_structure => [ 'db:structure:dump', 'db:test:purge' ] do abcs = ActiveRecord::Base.configurations - case abcs['test']['adapter'] + case abcs[env]['adapter'] when /mysql/ - ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.establish_connection(abcs[env]) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') - IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table| + IO.read("#{Rails.root}/db/structure.sql").split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end when /postgresql/ - ENV['PGHOST'] = abcs['test']['host'] if abcs['test']['host'] - ENV['PGPORT'] = abcs['test']['port'].to_s if abcs['test']['port'] - ENV['PGPASSWORD'] = abcs['test']['password'].to_s if abcs['test']['password'] - `psql -U "#{abcs['test']['username']}" -f "#{Rails.root}/db/#{Rails.env}_structure.sql" #{abcs['test']['database']} #{abcs['test']['template']}` + set_psql_env(abcs[env]) + `psql -f "#{Rails.root}/db/structure.sql" #{abcs[env]['database']} #{abcs[env]['template']}` when /sqlite/ - dbfile = abcs['test']['database'] || abcs['test']['dbfile'] - `sqlite3 #{dbfile} < "#{Rails.root}/db/#{Rails.env}_structure.sql"` + dbfile = abcs[env]['database'] + `sqlite3 #{dbfile} < "#{Rails.root}/db/structure.sql"` when 'sqlserver' - `sqlcmd -S #{abcs['test']['host']} -d #{abcs['test']['database']} -U #{abcs['test']['username']} -P #{abcs['test']['password']} -i db\\#{Rails.env}_structure.sql` + `sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i db\\structure.sql` when 'oci', 'oracle' - ActiveRecord::Base.establish_connection(:test) - IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl| + ActiveRecord::Base.establish_connection(abcs[env]) + IO.read("#{Rails.root}/db/structure.sql").split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end when 'firebird' - set_firebird_env(abcs['test']) - db_string = firebird_db_string(abcs['test']) - sh "isql -i #{Rails.root}/db/#{Rails.env}_structure.sql #{db_string}" + set_firebird_env(abcs[env]) + db_string = firebird_db_string(abcs[env]) + sh "isql -i #{Rails.root}/db/structure.sql #{db_string}" else - raise "Task not supported by '#{abcs['test']['adapter']}'" + raise "Task not supported by '#{abcs[env]['adapter']}'" + end + end + + task :load_if_sql => 'db:create' do + db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql + end + end + + namespace :test do + # desc "Recreate the test database from the current schema.rb" + task :load => 'db:test:purge' do + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) + ActiveRecord::Schema.verbose = false + db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby + + begin + old_env, ENV['RAILS_ENV'] = ENV['RAILS_ENV'], 'test' + db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql + ensure + ENV['RAILS_ENV'] = old_env end + end + # desc "Recreate the test database from the current environment's database schema" + task :clone => %w(db:schema:dump db:test:load) + + # desc "Recreate the test databases from the structure.sql file" + task :clone_structure => [ "db:structure:dump", "db:test:load" ] + # desc "Empty the test database" task :purge => :environment do abcs = ActiveRecord::Base.configurations @@ -447,7 +475,7 @@ db_namespace = namespace :db do drop_database(abcs['test']) create_database(abcs['test']) when /sqlite/ - dbfile = abcs['test']['database'] || abcs['test']['dbfile'] + dbfile = abcs['test']['database'] File.delete(dbfile) if File.exist?(dbfile) when 'sqlserver' test = abcs.deep_dup['test'] @@ -470,7 +498,7 @@ db_namespace = namespace :db do # desc 'Check for pending migrations and load the test schema' task :prepare => 'db:abort_if_pending_migrations' do - if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank? + unless ActiveRecord::Base.configurations.blank? db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke end end @@ -565,3 +593,10 @@ end def firebird_db_string(config) FireRuby::Database.db_string_for(config.symbolize_keys) end + +def set_psql_env(config) + ENV['PGHOST'] = config['host'] if config['host'] + ENV['PGPORT'] = config['port'].to_s if config['port'] + ENV['PGPASSWORD'] = config['password'].to_s if config['password'] + ENV['PGUSER'] = config['username'].to_s if config['username'] +end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index f0891440a6..0c32ad5139 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- require 'active_support/core_ext/object/blank' require 'active_support/core_ext/module/delegation' @@ -155,8 +156,8 @@ module ActiveRecord end queries.map do |sql| - @klass.connection.explain(sql) - end.join + "EXPLAIN for: #{sql}\n#{@klass.connection.explain(sql)}" + end.join("\n") end def to_a diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 7e8ddd1b5d..a789f48725 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -22,21 +22,23 @@ module ActiveRecord value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? attribute.in(value.arel.ast) when Array, ActiveRecord::Associations::CollectionProxy - values = value.to_a.map { |x| - x.is_a?(ActiveRecord::Base) ? x.id : x - } + values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x} + ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)} + + array_predicates = ranges.map {|range| attribute.in(range)} if values.include?(nil) values = values.compact if values.empty? - attribute.eq nil + array_predicates << attribute.eq(nil) else - attribute.in(values.compact).or attribute.eq(nil) + array_predicates << attribute.in(values.compact).or(attribute.eq(nil)) end else - attribute.in(values) + array_predicates << attribute.in(values) end + array_predicates.inject {|composite, predicate| composite.or(predicate)} when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index 5ad40d8cd9..c23514c465 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -2,7 +2,7 @@ module ActiveRecord #:nodoc: # = Active Record Serialization module Serialization extend ActiveSupport::Concern - include ActiveModel::Serializers::JSON + include ActiveModel::Serializable::JSON def serializable_hash(options = nil) options = options.try(:clone) || {} diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 0e7f57aa43..2da836ef0c 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/hash/conversions' module ActiveRecord #:nodoc: module Serialization - include ActiveModel::Serializers::Xml + include ActiveModel::Serializable::XML # Builds an XML document to represent the model. Some configuration is # available through +options+. However more complicated cases should @@ -176,13 +176,13 @@ module ActiveRecord #:nodoc: end end - class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc: + class XmlSerializer < ActiveModel::Serializable::XML::Serializer #:nodoc: def initialize(*args) super options[:except] = Array.wrap(options[:except]) | Array.wrap(@serializable.class.inheritance_column) end - class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc: + class Attribute < ActiveModel::Serializable::XML::Serializer::Attribute #:nodoc: def compute_type klass = @serializable.class type = if klass.serialized_attributes.key?(name) diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 92550c7efc..e3bbd06f7e 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -59,18 +59,17 @@ module ActiveRecord end def drop_table! - connection_pool.clear_table_cache!(table_name) + connection.schema_cache.clear_table_cache!(table_name) connection.drop_table table_name end def create_table! - id_col_name, data_col_name = session_id_column, data_column_name - connection_pool.clear_table_cache!(table_name) + connection.schema_cache.clear_table_cache!(table_name) connection.create_table(table_name) do |t| - t.string id_col_name, :limit => 255 - t.text data_col_name + t.string session_id_column, :limit => 255 + t.text data_column_name end - connection.add_index table_name, id_col_name, :unique => true + connection.add_index table_name, session_id_column, :unique => true end end diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb index 1c2942170e..267ea8bb6b 100644 --- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb +++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb @@ -9,11 +9,16 @@ module ActiveRecord class FakeAdapter < AbstractAdapter attr_accessor :tables, :primary_keys + @columns = Hash.new { |h,k| h[k] = [] } + class << self + attr_reader :columns + end + def initialize(connection, logger) super @tables = [] @primary_keys = {} - @columns = Hash.new { |h,k| h[k] = [] } + @columns = self.class.columns end def primary_key(table) diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 94497e37c7..f1023ed7ef 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -157,14 +157,4 @@ class AdapterTest < ActiveRecord::TestCase end end end - - def test_deprecated_visitor_for - visitor_klass = Class.new(Arel::Visitors::ToSql) - Arel::Visitors::VISITORS['fuuu'] = visitor_klass - pool = stub(:spec => stub(:config => { :adapter => 'fuuu' })) - visitor = assert_deprecated { - ActiveRecord::ConnectionAdapters::AbstractAdapter.visitor_for(pool) - } - assert visitor.is_a?(visitor_klass) - end end diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb index a2155d1dd1..1aa034ed53 100644 --- a/activerecord/test/cases/adapters/mysql/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/schema_test.rb @@ -13,7 +13,7 @@ module ActiveRecord table = Post.table_name @db_name = db - @omgpost = Class.new(Post) do + @omgpost = Class.new(ActiveRecord::Base) do set_table_name "#{db}.#{table}" def self.name; 'Post'; end end @@ -23,6 +23,10 @@ module ActiveRecord assert @omgpost.find(:first) end + def test_primary_key + assert_equal 'id', @omgpost.primary_key + end + def test_table_exists? name = @omgpost.table_name assert @connection.table_exists?(name), "#{name} table should exist" diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb index 8ea777b72b..68ed361aeb 100644 --- a/activerecord/test/cases/adapters/mysql2/explain_test.rb +++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb @@ -9,12 +9,15 @@ module ActiveRecord def test_explain_for_one_query explain = Developer.where(:id => 1).explain + assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain assert_match %(developers | const), explain end def test_explain_with_eager_loading explain = Developer.where(:id => 1).includes(:audit_logs).explain + assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain assert_match %(developers | const), explain + assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` IN (1)), explain assert_match %(audit_logs | ALL), explain end end diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 858d1da2dd..49514e1539 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -13,7 +13,7 @@ module ActiveRecord table = Post.table_name @db_name = db - @omgpost = Class.new(Post) do + @omgpost = Class.new(ActiveRecord::Base) do set_table_name "#{db}.#{table}" def self.name; 'Post'; end end @@ -23,6 +23,10 @@ module ActiveRecord assert @omgpost.find(:first) end + def test_primary_key + assert_equal 'id', @omgpost.primary_key + end + def test_table_exists? name = @omgpost.table_name assert @connection.table_exists?(name), "#{name} table should exist" diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb index 0d599ed37f..0b61f61572 100644 --- a/activerecord/test/cases/adapters/postgresql/explain_test.rb +++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb @@ -9,6 +9,7 @@ module ActiveRecord def test_explain_for_one_query explain = Developer.where(:id => 1).explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match %(QUERY PLAN), explain assert_match %(Index Scan using developers_pkey on developers), explain end @@ -16,7 +17,9 @@ module ActiveRecord def test_explain_with_eager_loading explain = Developer.where(:id => 1).includes(:audit_logs).explain assert_match %(QUERY PLAN), explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match %(Index Scan using developers_pkey on developers), explain + assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain assert_match %(Seq Scan on audit_logs), explain end end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 19669bdeb0..467e5d7b86 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -197,7 +197,7 @@ class SchemaTest < ActiveRecord::TestCase end def test_dump_indexes_for_schema_multiple_schemas_in_search_path - do_dump_index_tests_for_schema("public, #{SCHEMA_NAME}", INDEX_A_COLUMN, INDEX_B_COLUMN_S1) + do_dump_index_tests_for_schema("public, #{SCHEMA_NAME}", INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN) end def test_with_uppercase_index_name diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb index e18892821d..b227bce680 100644 --- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb @@ -9,12 +9,15 @@ module ActiveRecord def test_explain_for_one_query explain = Developer.where(:id => 1).explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain) end def test_explain_with_eager_loading explain = Developer.where(:id => 1).includes(:audit_logs).explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain) + assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain assert_match(/(SCAN )?TABLE audit_logs/, explain) end end diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index eb6f071dc1..97b56d38d7 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -158,7 +158,10 @@ module ActiveRecord binary.save! assert_equal str, binary.data - DualEncoding.connection.drop_table('dual_encodings') + ensure + if "<3".respond_to?(:encode) + DualEncoding.connection.drop_table('dual_encodings') + end end def test_execute diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index fdb656fe13..997c9e7e9d 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1761,6 +1761,14 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "The First Topic", topics(:first).becomes(Reply).title end + def test_becomes_includes_errors + company = Company.new(:name => nil) + assert !company.valid? + original_errors = company.errors + client = company.becomes(Client) + assert_equal original_errors, client.errors + end + def test_silence_sets_log_level_to_error_in_block original_logger = ActiveRecord::Base.logger log = StringIO.new diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb new file mode 100644 index 0000000000..79e842f5e1 --- /dev/null +++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb @@ -0,0 +1,55 @@ +require "cases/helper" + +module ActiveRecord + module ConnectionAdapters + class SchemaCacheTest < ActiveRecord::TestCase + def setup + connection = ActiveRecord::Base.connection + @cache = SchemaCache.new connection + + if in_memory_db? + connection.create_table :posts do |t| + t.integer :cololumn + end + end + end + + def test_primary_key + assert_equal 'id', @cache.primary_keys['posts'] + end + + def test_primary_key_for_non_existent_table + assert_equal 'id', @cache.primary_keys['omgponies'] + end + + def test_primary_key_is_set_on_columns + posts_columns = @cache.columns_hash['posts'] + assert posts_columns['id'].primary + + (posts_columns.keys - ['id']).each do |key| + assert !posts_columns[key].primary + end + end + + def test_caches_columns + columns = @cache.columns['posts'] + assert_equal columns, @cache.columns['posts'] + end + + def test_caches_columns_hash + columns_hash = @cache.columns_hash['posts'] + assert_equal columns_hash, @cache.columns_hash['posts'] + end + + def test_clearing_column_cache + @cache.columns['posts'] + @cache.columns_hash['posts'] + + @cache.clear! + + assert_equal 0, @cache.columns.size + assert_equal 0, @cache.columns_hash.size + end + end + end +end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 8a0f453127..1550fa5530 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -26,43 +26,6 @@ module ActiveRecord assert !@pool.active_connection? end - def test_pool_caches_columns - columns = @pool.columns['posts'] - assert_equal columns, @pool.columns['posts'] - end - - def test_pool_caches_columns_hash - columns_hash = @pool.columns_hash['posts'] - assert_equal columns_hash, @pool.columns_hash['posts'] - end - - def test_clearing_column_cache - @pool.columns['posts'] - @pool.columns_hash['posts'] - - @pool.clear_cache! - - assert_equal 0, @pool.columns.size - assert_equal 0, @pool.columns_hash.size - end - - def test_primary_key - assert_equal 'id', @pool.primary_keys['posts'] - end - - def test_primary_key_for_non_existent_table - assert_equal 'id', @pool.primary_keys['omgponies'] - end - - def test_primary_key_is_set_on_columns - posts_columns = @pool.columns_hash['posts'] - assert posts_columns['id'].primary - - (posts_columns.keys - ['id']).each do |key| - assert !posts_columns[key].primary - end - end - def test_clear_stale_cached_connections! pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 69754d23b9..05c4b15407 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -374,6 +374,10 @@ class FinderTest < ActiveRecord::TestCase assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort end + def test_find_on_hash_conditions_with_array_of_integers_and_ranges + assert_equal [1,2,3,5,6,7,8,9], Comment.find(:all, :conditions => {:id => [1..2, 3, 5, 6..8, 9]}).map(&:id).sort + end + def test_find_on_multiple_hash_conditions assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false }) assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) } diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index d6c7edc461..3e219f2a49 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -483,7 +483,7 @@ if ActiveRecord::Base.connection.supports_migrations? # Do a manual insertion if current_adapter?(:OracleAdapter) - Person.connection.execute "insert into people (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, 0, 0)" + Person.connection.execute "insert into people (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, sysdate, sysdate)" elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings Person.connection.execute "insert into people (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)" elsif current_adapter?(:PostgreSQLAdapter) @@ -1639,180 +1639,6 @@ if ActiveRecord::Base.connection.supports_migrations? end - class SexyMigrationsTest < ActiveRecord::TestCase - def test_references_column_type_adds_id - with_new_table do |t| - t.expects(:column).with('customer_id', :integer, {}) - t.references :customer - end - end - - def test_references_column_type_with_polymorphic_adds_type - with_new_table do |t| - t.expects(:column).with('taggable_type', :string, {}) - t.expects(:column).with('taggable_id', :integer, {}) - t.references :taggable, :polymorphic => true - end - end - - def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag - with_new_table do |t| - t.expects(:column).with('taggable_type', :string, {:null => false}) - t.expects(:column).with('taggable_id', :integer, {:null => false}) - t.references :taggable, :polymorphic => true, :null => false - end - end - - def test_belongs_to_works_like_references - with_new_table do |t| - t.expects(:column).with('customer_id', :integer, {}) - t.belongs_to :customer - end - end - - def test_timestamps_creates_updated_at_and_created_at - with_new_table do |t| - t.expects(:column).with(:created_at, :datetime, kind_of(Hash)) - t.expects(:column).with(:updated_at, :datetime, kind_of(Hash)) - t.timestamps - end - end - - def test_integer_creates_integer_column - with_new_table do |t| - t.expects(:column).with(:foo, 'integer', {}) - t.expects(:column).with(:bar, 'integer', {}) - t.integer :foo, :bar - end - end - - def test_string_creates_string_column - with_new_table do |t| - t.expects(:column).with(:foo, 'string', {}) - t.expects(:column).with(:bar, 'string', {}) - t.string :foo, :bar - end - end - - if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter) - def test_xml_creates_xml_column - type = current_adapter?(:PostgreSQLAdapter) ? 'xml' : :text - - with_new_table do |t| - t.expects(:column).with(:data, type, {}) - t.xml :data - end - end - else - def test_xml_creates_xml_column - with_new_table do |t| - assert_raises(NotImplementedError) do - t.xml :data - end - end - end - end - - protected - def with_new_table - Person.connection.create_table :delete_me, :force => true do |t| - yield t - end - ensure - Person.connection.drop_table :delete_me rescue nil - end - - end # SexyMigrationsTest - - class SexierMigrationsTest < ActiveRecord::TestCase - def test_create_table_with_column_without_block_parameter - Person.connection.create_table :testings, :force => true do - column :foo, :string - end - assert Person.connection.column_exists?(:testings, :foo, :string) - ensure - Person.connection.drop_table :testings rescue nil - end - - def test_create_table_with_sexy_column_without_block_parameter - Person.connection.create_table :testings, :force => true do - integer :bar - end - assert Person.connection.column_exists?(:testings, :bar, :integer) - ensure - Person.connection.drop_table :testings rescue nil - end - - def test_create_table_should_not_have_mixed_syntax - assert_raise(NoMethodError) do - Person.connection.create_table :testings, :force => true do |t| - t.string :foo - integer :bar - end - end - assert_raise(NameError) do - Person.connection.create_table :testings, :force => true do - t.string :foo - integer :bar - end - end - end - - def test_change_table_without_block_parameter_no_bulk - Person.connection.create_table :testings, :force => true do - string :foo - end - assert Person.connection.column_exists?(:testings, :foo, :string) - - Person.connection.change_table :testings do - remove :foo - integer :bar - end - - assert_equal %w(bar id), Person.connection.columns(:testings).map { |c| c.name }.sort - ensure - Person.connection.drop_table :testings rescue nil - end - - if ActiveRecord::Base.connection.supports_bulk_alter? - def test_change_table_without_block_parameter_with_bulk - Person.connection.create_table :testings, :force => true do - string :foo - end - assert Person.connection.column_exists?(:testings, :foo, :string) - - assert_queries(1) do - Person.connection.change_table(:testings, :bulk => true) do - integer :bar - string :foo_bar - end - end - - assert_equal %w(bar foo foo_bar id), Person.connection.columns(:testings).map { |c| c.name }.sort - ensure - Person.connection.drop_table :testings rescue nil - end - end - - def test_change_table_should_not_have_mixed_syntax - Person.connection.create_table :testings, :force => true do - string :foo - end - assert_raise(NoMethodError) do - Person.connection.change_table :testings do |t| - t.remove :foo - integer :bar - end - end - assert_raise(NameError) do - Person.connection.change_table :testings do - t.remove :foo - integer :bar - end - end - end - end # SexierMigrationsTest - class MigrationLoggerTest < ActiveRecord::TestCase def test_migration_should_be_run_without_logger previous_logger = ActiveRecord::Base.logger diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 434b8a677a..bc3dfb1078 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -66,78 +66,6 @@ class PooledConnectionsTest < ActiveRecord::TestCase assert_equal 1, ActiveRecord::Base.connection_pool.connections.size end - def test_pooled_connection_checkin_two - checkout_checkin_connections 2, 3 - assert_equal 3, @connection_count - assert_equal 0, @timed_out - assert_equal 1, ActiveRecord::Base.connection_pool.connections.size - end - - def test_pooled_connection_checkout_existing_first - ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1})) - conn_pool = ActiveRecord::Base.connection_pool - conn = conn_pool.checkout - conn_pool.checkin(conn) - conn = conn_pool.checkout - assert ActiveRecord::ConnectionAdapters::AbstractAdapter === conn - conn_pool.checkin(conn) - end - - def test_not_connected_defined_connection_returns_false - ActiveRecord::Base.establish_connection(@connection) - assert ! ActiveRecord::Base.connected? - end - - def test_undefined_connection_returns_false - old_handler = ActiveRecord::Base.connection_handler - ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new - assert ! ActiveRecord::Base.connected? - ensure - ActiveRecord::Base.connection_handler = old_handler - end - - def test_connection_config - ActiveRecord::Base.establish_connection(@connection) - assert_equal @connection, ActiveRecord::Base.connection_config - end - - def test_with_connection_nesting_safety - ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1, :wait_timeout => 0.1})) - - before_count = Project.count - - add_record('one') - - ActiveRecord::Base.connection.transaction do - add_record('two') - # Have another thread try to screw up the transaction - Thread.new do - ActiveRecord::Base.connection.rollback_db_transaction - ActiveRecord::Base.connection_pool.release_connection - end - add_record('three') - end - - after_count = Project.count - assert_equal 3, after_count - before_count - end - - def test_connection_pool_callbacks - checked_out, checked_in = false, false - ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do - set_callback(:checkout, :after) { checked_out = true } - set_callback(:checkin, :before) { checked_in = true } - end - @per_test_teardown << proc do - ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do - reset_callbacks :checkout - reset_callbacks :checkin - end - end - checkout_checkin_connections 1, 1 - assert checked_out - assert checked_in - end private |