diff options
author | Jon Leighton <j@jonathanleighton.com> | 2010-10-14 10:25:43 +0100 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2010-10-14 10:25:43 +0100 |
commit | 3fb493c2b037ffbdda5c91d66334ec6f79faa2d1 (patch) | |
tree | e49b072103bbfe6fb6159954c786a31f44099325 /activerecord | |
parent | 212fdd8ba9624f61421a7a950283537a3d39ac18 (diff) | |
parent | 01ab6f961bff150d50c99f03fa3946f48ac29b17 (diff) | |
download | rails-3fb493c2b037ffbdda5c91d66334ec6f79faa2d1.tar.gz rails-3fb493c2b037ffbdda5c91d66334ec6f79faa2d1.tar.bz2 rails-3fb493c2b037ffbdda5c91d66334ec6f79faa2d1.zip |
Merge branch 'master' into nested_has_many_through
Conflicts:
activerecord/lib/active_record/associations.rb
activerecord/test/cases/associations/cascaded_eager_loading_test.rb
Diffstat (limited to 'activerecord')
29 files changed, 450 insertions, 163 deletions
diff --git a/activerecord/RUNNING_UNIT_TESTS b/activerecord/RUNNING_UNIT_TESTS index 324df2c025..18e3936d8a 100644 --- a/activerecord/RUNNING_UNIT_TESTS +++ b/activerecord/RUNNING_UNIT_TESTS @@ -31,8 +31,13 @@ That'll run the base suite using the MySQL-Ruby adapter. Some tests rely on the being initialized - you can initialize the schema with: rake test_mysql TEST=test/cases/aaa_create_tables_test.rb + rake mysql:build_databases + +To setup the testing environment for PostgreSQL use this command: + + rake postgresql:build_databases The incantation for running a particular test looks like this - ruby -w -I"lib:test:test/connections/native_postgresql" test/cases/datatype_test_postgresql.rb -n test_timestamp_with_zone_values_without_rails_time_zone_support + rake test TEST=test/cases/datatype_test_postgresql.rb TESTOPTS="-n test_timestamp_with_zone_values_without_rails_time_zone_support" diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index f692e5ac89..c80bce2849 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -33,12 +33,12 @@ require 'active_support/i18n' require 'active_model' require 'arel' +require 'active_record/version' + module ActiveRecord extend ActiveSupport::Autoload eager_autoload do - autoload :VERSION - autoload :ActiveRecordError, 'active_record/errors' autoload :ConnectionNotEstablished, 'active_record/errors' diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index ff6be4ff19..78b3507dd9 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -423,7 +423,7 @@ module ActiveRecord #:nodoc: class << self # Class methods delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped - delegate :select, :group, :order, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped + delegate :select, :group, :order, :except, :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 37e584a629..ca9314ec99 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -73,12 +73,7 @@ module ActiveRecord # The mutex used to synchronize pool access @connection_mutex = Monitor.new @queue = @connection_mutex.new_cond - - # default 5 second timeout unless on ruby 1.9 - @timeout = - if RUBY_VERSION < '1.9' - spec.config[:wait_timeout] || 5 - end + @timeout = spec.config[:wait_timeout] || 5 # default max pool size to 5 @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5 @@ -161,7 +156,6 @@ module ActiveRecord keys = @reserved_connections.keys - Thread.list.find_all { |t| t.alive? }.map { |thread| thread.object_id } - keys.each do |key| checkin @reserved_connections[key] @reserved_connections.delete(key) @@ -194,16 +188,18 @@ module ActiveRecord checkout_new_connection end return conn if conn - # No connections available; wait for one - if @queue.wait(@timeout) + + @queue.wait(@timeout) + + if(@checked_out.size < @connections.size) next else - # try looting dead threads clear_stale_cached_connections! if @size == @checked_out.size raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it." end end + end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index e2b3773a99..a7a12faac2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -10,28 +10,31 @@ module ActiveRecord return value.quoted_id if value.respond_to?(:quoted_id) case value - when String, ActiveSupport::Multibyte::Chars - value = value.to_s - if column && column.type == :binary && column.class.respond_to?(:string_to_binary) - "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode) - elsif column && [:integer, :float].include?(column.type) - value = column.type == :integer ? value.to_i : value.to_f - value.to_s - else - "'#{quote_string(value)}'" # ' (for ruby-mode) - end - when NilClass then "NULL" - when TrueClass then (column && column.type == :integer ? '1' : quoted_true) - when FalseClass then (column && column.type == :integer ? '0' : quoted_false) - when Float, Fixnum, Bignum then value.to_s - # BigDecimals need to be output in a non-normalized form and quoted. - when BigDecimal then value.to_s('F') + when String, ActiveSupport::Multibyte::Chars + value = value.to_s + return "'#{quote_string(value)}'" unless column + + case column.type + when :binary then "'#{quote_string(column.string_to_binary(value))}'" + when :integer then value.to_i.to_s + when :float then value.to_f.to_s + else + "'#{quote_string(value)}'" + end + + when true, false + if column && column.type == :integer + value ? '1' : '0' else - if value.acts_like?(:date) || value.acts_like?(:time) - "'#{quoted_date(value)}'" - else - "'#{quote_string(value.to_s)}'" - end + value ? quoted_true : quoted_false + end + # BigDecimals need to be put in a non-normalized form and quoted. + when nil then "NULL" + when BigDecimal then value.to_s('F') + when Numeric then value.to_s + when Date, Time then "'#{quoted_date(value)}'" + else + "'#{quote_string(value.to_s)}'" end end 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 6480aeb171..60ccf9edf3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -114,6 +114,11 @@ module ActiveRecord type_cast(default) end + # Used to convert from Strings to BLOBs + def string_to_binary(value) + self.class.string_to_binary(value) + end + class << self # Used to convert from Strings to BLOBs def string_to_binary(value) @@ -268,6 +273,10 @@ module ActiveRecord # 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 string_to_binary(value) + value + end + def sql_type base.type_to_sql(type.to_sym, limit, precision, scale) rescue type end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index e7f7b37b27..a4b336dfaf 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -132,7 +132,7 @@ module ActiveRecord cattr_accessor :emulate_booleans self.emulate_booleans = true - ADAPTER_NAME = 'MySQL'.freeze + ADAPTER_NAME = 'MySQL' LOST_CONNECTION_ERROR_MESSAGES = [ "Server shutdown in progress", @@ -140,10 +140,10 @@ module ActiveRecord "Lost connection to MySQL server during query", "MySQL server has gone away" ] - QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze + QUOTED_TRUE, QUOTED_FALSE = '1', '0' NATIVE_DATABASE_TYPES = { - :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze, + :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", :string => { :name => "varchar", :limit => 255 }, :text => { :name => "text" }, :integer => { :name => "int", :limit => 4 }, diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 5f14284615..dce9e99d27 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -180,8 +180,6 @@ module ActiveRecord # <encoding></tt> call on the connection. # * <tt>:min_messages</tt> - An optional client min messages that is used in a # <tt>SET client_min_messages TO <min_messages></tt> call on the connection. - # * <tt>:allow_concurrency</tt> - If true, use async query methods so Ruby threads don't deadlock; - # otherwise, use blocking query methods. class PostgreSQLAdapter < AbstractAdapter class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition def xml(*args) @@ -930,7 +928,7 @@ module ActiveRecord PGconn.translate_results = false if PGconn.respond_to?(:translate_results=) # Ignore async_exec and async_query when using postgres-pr. - @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec) + @async = @connection.respond_to?(:async_exec) # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 9ac18f9939..a4c09b654a 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -384,23 +384,32 @@ module ActiveRecord end end - def copy(destination, sources) + def copy(destination, sources, options = {}) copied = [] - sources.each do |scope, path| - destination_migrations = ActiveRecord::Migrator.migrations(destination) + destination_migrations = ActiveRecord::Migrator.migrations(destination) + last = destination_migrations.last + sources.each do |name, path| source_migrations = ActiveRecord::Migrator.migrations(path) - last = destination_migrations.last source_migrations.each do |migration| - next if destination_migrations.any? { |m| m.name == migration.name && m.scope == scope.to_s } + source = File.read(migration.filename) + source = "# This migration comes from #{name} (originally #{migration.version})\n#{source}" + + if duplicate = destination_migrations.detect { |m| m.name == migration.name } + options[:on_skip].call(name, migration) if File.read(duplicate.filename) != source && options[:on_skip] + next + end migration.version = next_migration_number(last ? last.version + 1 : 0).to_i + new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb") + old_path, migration.filename = migration.filename, new_path last = migration - new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb") - FileUtils.cp(migration.filename, new_path) - copied << new_path + FileUtils.cp(old_path, migration.filename) + copied << migration + options[:on_copy].call(name, migration, old_path) if options[:on_copy] + destination_migrations << migration end end @@ -419,13 +428,17 @@ module ActiveRecord # MigrationProxy is used to defer loading of the actual migration classes # until they are needed - class MigrationProxy < Struct.new(:name, :version, :filename, :scope) + class MigrationProxy < Struct.new(:name, :version, :filename) - def initialize(name, version, filename, scope) + def initialize(name, version, filename) super @migration = nil end + def basename + File.basename(filename) + end + delegate :migrate, :announce, :write, :to=>:migration private @@ -510,18 +523,18 @@ module ActiveRecord seen = Hash.new false migrations = files.map do |file| - version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first + version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first raise IllegalMigrationNameError.new(file) unless version version = version.to_i name = name.camelize raise DuplicateMigrationVersionError.new(version) if seen[version] - raise DuplicateMigrationNameError.new(name) if seen[[name, scope]] + raise DuplicateMigrationNameError.new(name) if seen[name] - seen[version] = seen[[name, scope]] = true + seen[version] = seen[name] = true - MigrationProxy.new(name, version, file, scope) + MigrationProxy.new(name, version, file) end migrations.sort_by(&:version) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 58c705c8b2..1fbc8a1d32 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -2,28 +2,7 @@ namespace :db do task :load_config => :rails_env do require 'active_record' ActiveRecord::Base.configurations = Rails.application.config.database_configuration - ActiveRecord::Migrator.migrations_path = Rails.application.config.paths.db.migrate.to_a.first - end - - task :copy_migrations => :load_config do - to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map {|n| n.strip } - railties = {} - Rails.application.railties.all do |railtie| - next unless to_load == :all || to_load.include?(railtie.railtie_name) - - if railtie.config.respond_to?(:paths) && railtie.config.paths.db - railties[railtie.railtie_name] = railtie.config.paths.db.migrate.to_a.first - end - end - - copied = ActiveRecord::Migration.copy(ActiveRecord::Migrator.migrations_path, railties) - - if copied.blank? - puts "No migrations were copied, project is up to date." - else - puts "The following migrations were copied:" - puts copied.map{ |path| File.basename(path) }.join("\n") - end + ActiveRecord::Migrator.migrations_path = Rails.application.paths["db/migrate"].first end namespace :create do @@ -501,8 +480,31 @@ namespace :db do end namespace :railties do - desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2" - task :copy_migrations => 'db:copy_migrations' + namespace :install do + # desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2" + task :migrations => :"db:load_config" do + to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map {|n| n.strip } + railties = {} + Rails.application.railties.all do |railtie| + next unless to_load == :all || to_load.include?(railtie.railtie_name) + + if railtie.respond_to?(:paths) && (path = railtie.paths["db/migrate"].first) + railties[railtie.railtie_name] = path + end + end + + on_skip = Proc.new do |name, migration| + puts "NOTE: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists." + end + + on_copy = Proc.new do |name, migration, old_path| + puts "Copied migration #{migration.basename} from #{name}" + end + + ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_path, railties, + :on_skip => on_skip, :on_copy => on_copy) + end + end end task 'test:prepare' => 'db:test:prepare' diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index d79ef78b4d..6bf698fe97 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -177,7 +177,7 @@ module ActiveRecord distinct = options[:distinct] || distinct if @group_values.any? - execute_grouped_calculation(operation, column_name) + execute_grouped_calculation(operation, column_name, distinct) else execute_simple_calculation(operation, column_name, distinct) end @@ -191,19 +191,23 @@ module ActiveRecord end end + def operation_over_aggregate_column(column, operation, distinct) + operation == 'count' ? column.count(distinct) : column.send(operation) + 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 == 'count' ? column.count(distinct) : column.send(operation) + select_value = operation_over_aggregate_column(column, operation, distinct) relation.select_values = [select_value] type_cast_calculated_value(@klass.connection.select_value(relation.to_sql), column_for(column_name), operation) end - def execute_grouped_calculation(operation, column_name) #:nodoc: + def execute_grouped_calculation(operation, column_name, distinct) #:nodoc: group_attr = @group_values.first association = @klass.reflect_on_association(group_attr.to_sym) associated = association && association.macro == :belongs_to # only count belongs_to associations @@ -221,7 +225,7 @@ module ActiveRecord relation = except(:group).group(group) relation.select_values = [ - aggregate_column(column_name).send(operation).as(aggregate_alias), + operation_over_aggregate_column(aggregate_column(column_name), operation, distinct).as(aggregate_alias), "#{group_field} AS #{group_alias}" ] @@ -273,7 +277,7 @@ module ActiveRecord else type_cast_using_column(value, column) end else - value + type_cast_using_column(value, column) end end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 5034caf084..b763e22ec6 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -291,8 +291,8 @@ module ActiveRecord record = where(primary_key.eq(id)).first unless record - conditions = arel.wheres.map { |x| x.value }.join(', ') - conditions = " [WHERE #{conditions}]" if conditions.present? + conditions = arel.where_sql + conditions = " [#{conditions}]" if conditions raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}" end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index f314ff861f..59ce76ea42 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -6,7 +6,7 @@ module ActiveRecord extend ActiveSupport::Concern attr_accessor :includes_values, :eager_load_values, :preload_values, - :select_values, :group_values, :order_values, :reorder_flag, :joins_values, :where_values, :having_values, + :select_values, :group_values, :order_values, :joins_values, :where_values, :having_values, :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value def includes(*args) @@ -53,15 +53,6 @@ module ActiveRecord relation end - def reorder(*args) - relation = clone - unless args.blank? - relation.order_values = args - relation.reorder_flag = true - end - relation - end - def joins(*args) relation = clone diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 9ecdb99bee..dcddc3dac4 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -19,20 +19,11 @@ module ActiveRecord end end - (Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method| + (Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method| value = r.send(:"#{method}_values") merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present? end - order_value = r.order_values - if order_value.present? - if r.reorder_flag - merged_relation.order_values = order_value - else - merged_relation.order_values = merged_relation.order_values + order_value - end - end - merged_relation = merged_relation.joins(r.joins_values) merged_wheres = @where_values diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index 398eb1534a..2bde06f562 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -22,7 +22,7 @@ module ActiveRecord #:nodoc: end private - # Add associations specified via the <tt>:includes</tt> option. + # Add associations specified via the <tt>:include</tt> option. # # Expects a block that takes as arguments: # +association+ - name of the association diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index 31b5a8651b..014a900c71 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -36,21 +36,6 @@ module ActiveRecord assert_queries(0, &block) end - def self.use_concurrent_connections - setup :connection_allow_concurrency_setup - teardown :connection_allow_concurrency_teardown - end - - def connection_allow_concurrency_setup - @connection = ActiveRecord::Base.remove_connection - ActiveRecord::Base.establish_connection(@connection.merge({:allow_concurrency => true})) - end - - def connection_allow_concurrency_teardown - ActiveRecord::Base.clear_all_connections! - ActiveRecord::Base.establish_connection(@connection) - end - def with_kcode(kcode) if RUBY_VERSION < '1.9' orig_kcode, $KCODE = $KCODE, kcode diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 7ec40906d4..61fbf01a50 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -346,4 +346,13 @@ class CalculationsTest < ActiveRecord::TestCase def test_from_option_with_table_different_than_class assert_equal Account.count(:all), Company.count(:all, :from => 'accounts') end + + def test_distinct_is_honored_when_used_with_count_operation_after_group + # Count the number of authors for approved topics + approved_topics_count = Topic.group(:approved).count(:author_name)[true] + assert_equal approved_topics_count, 3 + # Count the number of distinct authors for approved Topics + distinct_authors_for_approved_count = Topic.group(:approved).count(:author_name, :distinct => true)[true] + assert_equal distinct_authors_for_approved_count, 2 + end end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 82b3c36ed2..f0ec5c751c 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -26,6 +26,35 @@ module ActiveRecord "threads should have been removed") assert_equal pool.checkins.length, threads.length end + + def test_checkout_behaviour + pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec + connection = pool.connection + assert_not_nil connection + threads = [] + 4.times do |i| + threads << Thread.new(i) do |pool_count| + connection = pool.connection + assert_not_nil connection + end + end + + threads.each {|t| t.join} + + Thread.new do + threads.each do |t| + thread_ids = pool.instance_variable_get(:@reserved_connections).keys + assert thread_ids.include?(t.object_id) + end + + pool.connection + threads.each do |t| + thread_ids = pool.instance_variable_get(:@reserved_connections).keys + assert !thread_ids.include?(t.object_id) + end + end.join() + + end end end end diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index e7126964cd..4ddcdc010b 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -257,6 +257,7 @@ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter) fixtures :people, :readers def setup + Person.connection_pool.clear_reloadable_connections! # Avoid introspection queries during tests. Person.columns; Reader.columns end @@ -306,8 +307,6 @@ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter) end if current_adapter?(:PostgreSQLAdapter, :OracleAdapter) - use_concurrent_connections - def test_no_locks_no_wait first, second = duel { Person.find 1 } assert first.end > second.end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 6e8ee95613..ef949300b0 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1910,9 +1910,9 @@ if ActiveRecord::Base.connection.supports_migrations? @existing_migrations = Dir[@migrations_path + "/*.rb"] copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"}) - assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb") - assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb") - assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied + assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb") + assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb") + assert_equal [@migrations_path + "/4_people_have_hobbies.rb", @migrations_path + "/5_people_have_descriptions.rb"], copied.map(&:filename) files_count = Dir[@migrations_path + "/*.rb"].length copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"}) @@ -1928,12 +1928,13 @@ if ActiveRecord::Base.connection.supports_migrations? @existing_migrations = Dir[@migrations_path + "/*.rb"] sources = ActiveSupport::OrderedHash.new - sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy" + sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy" + sources[:omg] = MIGRATIONS_ROOT + "/to_copy2" ActiveRecord::Migration.copy(@migrations_path, sources) - assert File.exists?(@migrations_path + "/4_people_have_hobbies.omg.rb") - assert File.exists?(@migrations_path + "/5_people_have_descriptions.omg.rb") - assert File.exists?(@migrations_path + "/6_people_have_hobbies.bukkits.rb") - assert File.exists?(@migrations_path + "/7_people_have_descriptions.bukkits.rb") + assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb") + assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb") + assert File.exists?(@migrations_path + "/6_create_articles.rb") + assert File.exists?(@migrations_path + "/7_create_comments.rb") files_count = Dir[@migrations_path + "/*.rb"].length ActiveRecord::Migration.copy(@migrations_path, sources) @@ -1948,11 +1949,11 @@ if ActiveRecord::Base.connection.supports_migrations? Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) - assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb") - assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb") - expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb", - @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"] - assert_equal expected, copied + assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb") + assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb") + expected = [@migrations_path + "/20100726101010_people_have_hobbies.rb", + @migrations_path + "/20100726101011_people_have_descriptions.rb"] + assert_equal expected, copied.map(&:filename) files_count = Dir[@migrations_path + "/*.rb"].length copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) @@ -1968,14 +1969,15 @@ if ActiveRecord::Base.connection.supports_migrations? @existing_migrations = Dir[@migrations_path + "/*.rb"] sources = ActiveSupport::OrderedHash.new - sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps" + sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps" + sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2" Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do copied = ActiveRecord::Migration.copy(@migrations_path, sources) - assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.omg.rb") - assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.omg.rb") - assert File.exists?(@migrations_path + "/20100726101012_people_have_hobbies.bukkits.rb") - assert File.exists?(@migrations_path + "/20100726101013_people_have_descriptions.bukkits.rb") + assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb") + assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb") + assert File.exists?(@migrations_path + "/20100726101012_create_articles.rb") + assert File.exists?(@migrations_path + "/20100726101013_create_comments.rb") assert_equal 4, copied.length files_count = Dir[@migrations_path + "/*.rb"].length @@ -1992,8 +1994,8 @@ if ActiveRecord::Base.connection.supports_migrations? Time.travel_to(created_at = Time.utc(2010, 2, 20, 10, 10, 10)) do ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) - assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb") - assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb") + assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.rb") + assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.rb") files_count = Dir[@migrations_path + "/*.rb"].length copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) @@ -2004,14 +2006,32 @@ if ActiveRecord::Base.connection.supports_migrations? clear end + def test_skipping_migrations + @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps" + @existing_migrations = Dir[@migrations_path + "/*.rb"] + + sources = ActiveSupport::OrderedHash.new + sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps" + + skipped = [] + on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" } + copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip) + assert_equal 2, copied.length + + assert_equal 2, skipped.length + assert_equal ["bukkits PeopleHaveHobbies", "bukkits PeopleHaveDescriptions"], skipped + ensure + clear + end + def test_copying_migrations_to_empty_directory @migrations_path = MIGRATIONS_ROOT + "/empty" @existing_migrations = [] Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) - assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb") - assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb") + assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb") + assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb") assert_equal 2, copied.length end ensure diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb new file mode 100644 index 0000000000..2ef5b5a800 --- /dev/null +++ b/activerecord/test/cases/quoting_test.rb @@ -0,0 +1,220 @@ +require "cases/helper" + +module ActiveRecord + module ConnectionAdapters + class QuotingTest < ActiveRecord::TestCase + class FakeColumn < ActiveRecord::ConnectionAdapters::Column + attr_accessor :type + + def initialize type + @type = type + end + end + + def setup + @quoter = Class.new { include Quoting }.new + end + + def test_quoted_true + assert_equal "'t'", @quoter.quoted_true + end + + def test_quoted_false + assert_equal "'f'", @quoter.quoted_false + end + + def test_quote_column_name + assert_equal "foo", @quoter.quote_column_name('foo') + end + + def test_quote_table_name + assert_equal "foo", @quoter.quote_table_name('foo') + end + + def test_quote_table_name_calls_quote_column_name + @quoter.extend(Module.new { + def quote_column_name(string) + 'lol' + end + }) + assert_equal 'lol', @quoter.quote_table_name('foo') + end + + def test_quote_string + assert_equal "''", @quoter.quote_string("'") + assert_equal "\\\\", @quoter.quote_string("\\") + assert_equal "hi''i", @quoter.quote_string("hi'i") + assert_equal "hi\\\\i", @quoter.quote_string("hi\\i") + end + + def test_quoted_date + t = Date.today + assert_equal t.to_s(:db), @quoter.quoted_date(t) + end + + def test_quoted_time_utc + before = ActiveRecord::Base.default_timezone + ActiveRecord::Base.default_timezone = :utc + t = Time.now + assert_equal t.getutc.to_s(:db), @quoter.quoted_date(t) + ensure + ActiveRecord::Base.default_timezone = before + end + + def test_quoted_time_local + before = ActiveRecord::Base.default_timezone + ActiveRecord::Base.default_timezone = :local + t = Time.now + assert_equal t.getlocal.to_s(:db), @quoter.quoted_date(t) + ensure + ActiveRecord::Base.default_timezone = before + end + + def test_quoted_time_crazy + before = ActiveRecord::Base.default_timezone + ActiveRecord::Base.default_timezone = :asdfasdf + t = Time.now + assert_equal t.getlocal.to_s(:db), @quoter.quoted_date(t) + ensure + ActiveRecord::Base.default_timezone = before + end + + def test_quoted_datetime_utc + before = ActiveRecord::Base.default_timezone + ActiveRecord::Base.default_timezone = :utc + t = DateTime.now + assert_equal t.getutc.to_s(:db), @quoter.quoted_date(t) + ensure + ActiveRecord::Base.default_timezone = before + end + + ### + # DateTime doesn't define getlocal, so make sure it does nothing + def test_quoted_datetime_local + before = ActiveRecord::Base.default_timezone + ActiveRecord::Base.default_timezone = :local + t = DateTime.now + assert_equal t.to_s(:db), @quoter.quoted_date(t) + ensure + ActiveRecord::Base.default_timezone = before + end + + def test_quote_with_quoted_id + assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1), nil) + assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1), 'foo') + end + + def test_quote_nil + assert_equal 'NULL', @quoter.quote(nil, nil) + assert_equal 'NULL', @quoter.quote(nil, 'foo') + end + + def test_quote_true + assert_equal @quoter.quoted_true, @quoter.quote(true, nil) + assert_equal '1', @quoter.quote(true, Struct.new(:type).new(:integer)) + end + + def test_quote_false + assert_equal @quoter.quoted_false, @quoter.quote(false, nil) + assert_equal '0', @quoter.quote(false, Struct.new(:type).new(:integer)) + end + + def test_quote_float + float = 1.2 + assert_equal float.to_s, @quoter.quote(float, nil) + assert_equal float.to_s, @quoter.quote(float, Object.new) + end + + def test_quote_fixnum + fixnum = 1 + assert_equal fixnum.to_s, @quoter.quote(fixnum, nil) + assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new) + end + + def test_quote_bignum + bignum = 1 << 100 + assert_equal bignum.to_s, @quoter.quote(bignum, nil) + assert_equal bignum.to_s, @quoter.quote(bignum, Object.new) + end + + def test_quote_bigdecimal + bigdec = BigDecimal.new((1 << 100).to_s) + assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, nil) + assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new) + end + + def test_dates_and_times + @quoter.extend(Module.new { def quoted_date(value) 'lol' end }) + assert_equal "'lol'", @quoter.quote(Date.today, nil) + assert_equal "'lol'", @quoter.quote(Date.today, Object.new) + assert_equal "'lol'", @quoter.quote(Time.now, nil) + assert_equal "'lol'", @quoter.quote(Time.now, Object.new) + assert_equal "'lol'", @quoter.quote(DateTime.now, nil) + assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new) + end + + def test_crazy_object + crazy = Class.new { def to_s; 'lol' end }.new + assert_equal "'lol'", @quoter.quote(crazy, nil) + assert_equal "'lol'", @quoter.quote(crazy, Object.new) + end + + def test_crazy_object_calls_quote_string + crazy = Class.new { def to_s; 'lo\l' end }.new + assert_equal "'lo\\\\l'", @quoter.quote(crazy, nil) + assert_equal "'lo\\\\l'", @quoter.quote(crazy, Object.new) + end + + def test_quote_string_no_column + assert_equal "'lo\\\\l'", @quoter.quote('lo\l', nil) + end + + def test_quote_as_mb_chars_no_column + string = ActiveSupport::Multibyte::Chars.new('lo\l') + assert_equal "'lo\\\\l'", @quoter.quote(string, nil) + end + + def test_quote_string_int_column + assert_equal "1", @quoter.quote('1', FakeColumn.new(:integer)) + assert_equal "1", @quoter.quote('1.2', FakeColumn.new(:integer)) + end + + def test_quote_string_float_column + assert_equal "1.0", @quoter.quote('1', FakeColumn.new(:float)) + assert_equal "1.2", @quoter.quote('1.2', FakeColumn.new(:float)) + end + + def test_quote_as_mb_chars_binary_column + string = ActiveSupport::Multibyte::Chars.new('lo\l') + assert_equal "'lo\\\\l'", @quoter.quote(string, FakeColumn.new(:binary)) + end + + def test_quote_binary_without_string_to_binary + assert_equal "'lo\\\\l'", @quoter.quote('lo\l', FakeColumn.new(:binary)) + end + + def test_quote_binary_with_string_to_binary + col = Class.new(FakeColumn) { + def string_to_binary(value) + 'foo' + end + }.new(:binary) + assert_equal "'foo'", @quoter.quote('lo\l', col) + end + + def test_quote_as_mb_chars_binary_column_with_string_to_binary + col = Class.new(FakeColumn) { + def string_to_binary(value) + 'foo' + end + }.new(:binary) + string = ActiveSupport::Multibyte::Chars.new('lo\l') + assert_equal "'foo'", @quoter.quote(string, col) + end + + def test_string_with_crazy_column + assert_equal "'lo\\\\l'", @quoter.quote('lo\l', FakeColumn.new(:foo)) + end + end + end +end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index 090e6c8bc9..64365c1d75 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -384,18 +384,12 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end - def test_reorder_overrides_default_scope_order + def test_except_and_order_overrides_default_scope_order expected = Developer.order('name DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.reorder('name DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.except(:order).order('name DESC').collect { |dev| dev.name } assert_equal expected, received end - def test_reordered_scope_overrides_default_scope_order - not_expected = DeveloperOrderedBySalary.first # Jamis -> name DESC - received = DeveloperOrderedBySalary.reordered_by_name.first # David -> name - assert not_expected.id != received.id - end - def test_nested_exclusive_scope expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary } received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index fec5e6731f..df4e84ca29 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -135,12 +135,6 @@ class RelationTest < ActiveRecord::TestCase assert_equal topics(:fourth).title, topics.first.title end - def test_finding_with_reorder - topics = Topic.order('author_name').order('title').reorder('id') - assert_equal 4, topics.to_a.size - assert_equal topics(:first).title, topics.first.title - end - def test_finding_with_order_and_take entrants = Entrant.order("id ASC").limit(2).to_a diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 9255190613..8385286fd0 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -529,8 +529,6 @@ end if Topic.connection.supports_savepoints? if current_adapter?(:PostgreSQLAdapter) class ConcurrentTransactionTest < TransactionTest - use_concurrent_connections - # This will cause transactions to overlap and fail unless they are performed on # separate database connections. def test_transaction_per_thread diff --git a/activerecord/test/migrations/to_copy2/1_create_articles.rb b/activerecord/test/migrations/to_copy2/1_create_articles.rb new file mode 100644 index 0000000000..0f048d90f7 --- /dev/null +++ b/activerecord/test/migrations/to_copy2/1_create_articles.rb @@ -0,0 +1,7 @@ +class CreateArticles < ActiveRecord::Migration + def self.up + end + + def self.down + end +end diff --git a/activerecord/test/migrations/to_copy2/2_create_comments.rb b/activerecord/test/migrations/to_copy2/2_create_comments.rb new file mode 100644 index 0000000000..0f048d90f7 --- /dev/null +++ b/activerecord/test/migrations/to_copy2/2_create_comments.rb @@ -0,0 +1,7 @@ +class CreateArticles < ActiveRecord::Migration + def self.up + end + + def self.down + end +end diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb new file mode 100644 index 0000000000..0f048d90f7 --- /dev/null +++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb @@ -0,0 +1,7 @@ +class CreateArticles < ActiveRecord::Migration + def self.up + end + + def self.down + end +end diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb new file mode 100644 index 0000000000..2b048edbb5 --- /dev/null +++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb @@ -0,0 +1,7 @@ +class CreateComments < ActiveRecord::Migration + def self.up + end + + def self.down + end +end diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 947583af76..32d060cf09 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -88,7 +88,6 @@ class DeveloperOrderedBySalary < ActiveRecord::Base self.table_name = 'developers' default_scope :order => 'salary DESC' scope :by_name, order('name DESC') - scope :reordered_by_name, reorder('name') def self.all_ordered_by_name with_scope(:find => { :order => 'name DESC' }) do |