diff options
Diffstat (limited to 'activerecord')
46 files changed, 795 insertions, 555 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index b27c03d935..55773f2172 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,61 @@ +* Use `max_identifier_length` for `index_name_length` in PostgreSQL adapter. + + *Ryuta Kamizono* + +* Deprecate `supports_migrations?` on connection adapters. + + *Ryuta Kamizono* + +* Fix regression of #1969 with SELECT aliases in HAVING clause. + + *Eugene Kenny* + +* Deprecate using `#quoted_id` in quoting. + + *Ryuta Kamizono* + +* Fix `wait_timeout` to configurable for mysql2 adapter. + + Fixes #26556. + + *Ryuta Kamizono* + + +## Rails 5.1.0.beta1 (February 23, 2017) ## + +* Correctly dump native timestamp types for MySQL. + + The native timestamp type in MySQL is different from datetime type. + Internal representation of the timestamp type is UNIX time, This means + that timestamp columns are affected by time zone. + + > SET time_zone = '+00:00'; + Query OK, 0 rows affected (0.00 sec) + + > INSERT INTO time_with_zone(ts,dt) VALUES (NOW(),NOW()); + Query OK, 1 row affected (0.02 sec) + + > SELECT * FROM time_with_zone; + +---------------------+---------------------+ + | ts | dt | + +---------------------+---------------------+ + | 2016-02-07 22:11:44 | 2016-02-07 22:11:44 | + +---------------------+---------------------+ + 1 row in set (0.00 sec) + + > SET time_zone = '-08:00'; + Query OK, 0 rows affected (0.00 sec) + + > SELECT * FROM time_with_zone; + +---------------------+---------------------+ + | ts | dt | + +---------------------+---------------------+ + | 2016-02-07 14:11:44 | 2016-02-07 22:11:44 | + +---------------------+---------------------+ + 1 row in set (0.00 sec) + + *Ryuta Kamizono* + * All integer-like PKs are autoincrement unless they have an explicit default. *Matthew Draper* diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 31c1e687dc..6aa414ba6b 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -50,7 +50,7 @@ module ActiveRecord super.tap do @previous_mutation_tracker = nil clear_mutation_trackers - @changed_attributes = HashWithIndifferentAccess.new + @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new end end @@ -70,13 +70,13 @@ module ActiveRecord def changes_applied @previous_mutation_tracker = mutation_tracker - @changed_attributes = HashWithIndifferentAccess.new + @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new clear_mutation_trackers end def clear_changes_information @previous_mutation_tracker = nil - @changed_attributes = HashWithIndifferentAccess.new + @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new forget_attribute_assignments clear_mutation_trackers end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 7f4132accf..e5a24b2aca 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -7,8 +7,13 @@ module ActiveRecord # Quotes the column value to help prevent # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection]. def quote(value) - # records are quoted as their primary key - return value.quoted_id if value.respond_to?(:quoted_id) + value = id_value_for_database(value) if value.is_a?(Base) + + if value.respond_to?(:quoted_id) + ActiveSupport::Deprecation.warn \ + "Using #quoted_id is deprecated and will be removed in Rails 5.2." + return value.quoted_id + end _quote(value) end @@ -17,6 +22,8 @@ module ActiveRecord # SQLite does not understand dates, so this method will convert a Date # to a String. def type_cast(value, column = nil) + value = id_value_for_database(value) if value.is_a?(Base) + if value.respond_to?(:quoted_id) && value.respond_to?(:id) return value.id end @@ -151,6 +158,12 @@ module ActiveRecord binds.map { |attr| type_cast(attr.value_for_database) } end + def id_value_for_database(value) + if primary_key = value.class.primary_key + value.instance_variable_get(:@attributes)[primary_key].value_for_database + end + end + def types_which_need_no_typecasting [nil, Numeric, String] end 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 3686ad8b54..e683106527 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -334,18 +334,16 @@ module ActiveRecord # part_id int NOT NULL, # ) ENGINE=InnoDB DEFAULT CHARSET=utf8 # - def create_join_table(table_1, table_2, options = {}) + def create_join_table(table_1, table_2, column_options: {}, **options) join_table_name = find_join_table_name(table_1, table_2, options) - column_options = options.delete(:column_options) || {} - column_options.reverse_merge!(null: false) - type = column_options.delete(:type) || :integer + column_options.reverse_merge!(null: false, index: false) - t1_column, t2_column = [table_1, table_2].map { |t| t.to_s.singularize.foreign_key } + t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize } create_table(join_table_name, options.merge!(id: false)) do |td| - td.send type, t1_column, column_options - td.send type, t2_column, column_options + td.references t1_ref, column_options + td.references t2_ref, column_options yield td if block_given? end end @@ -857,6 +855,7 @@ module ActiveRecord else foreign_key_options = { to_table: reference_name } end + foreign_key_options[:column] ||= "#{ref_name}_id" remove_foreign_key(table_name, **foreign_key_options) end @@ -1071,7 +1070,7 @@ module ActiveRecord raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified" end - elsif [:datetime, :time, :interval].include?(type) && precision ||= native[:precision] + elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision] if (0..6) === precision column_type_sql << "(#{precision})" else diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index b31ce0a181..ef1d9f81a9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -154,8 +154,8 @@ module ActiveRecord Arel::Visitors::ToSql.new(self) end - def valid_type?(type) - false + def valid_type?(type) # :nodoc: + !native_database_types[type].nil? end def schema_creation @@ -232,10 +232,10 @@ module ActiveRecord self.class::ADAPTER_NAME end - # Does this adapter support migrations? - def supports_migrations? - false + def supports_migrations? # :nodoc: + true end + deprecate :supports_migrations? def supports_primary_key? # :nodoc: true @@ -439,6 +439,9 @@ module ActiveRecord # This is done under the hood by calling #active?. If the connection # is no longer active, then this method will reconnect to the database. def verify!(*ignored) + if ignored.size > 0 + ActiveSupport::Deprecation.warn("Passing arguments to #verify method of the connection has no effect and has been deprecated. Please remove all arguments from the #verify method call.") + end reconnect! unless active? end 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 14269b4570..e3b6327dd8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -46,6 +46,7 @@ module ActiveRecord float: { name: "float" }, decimal: { name: "decimal" }, datetime: { name: "datetime" }, + timestamp: { name: "timestamp" }, time: { name: "time" }, date: { name: "date" }, binary: { name: "blob", limit: 65535 }, @@ -88,11 +89,6 @@ module ActiveRecord /mariadb/i.match?(full_version) end - # Returns true, since this connection adapter supports migrations. - def supports_migrations? - true - end - def supports_bulk_alter? #:nodoc: true end @@ -647,10 +643,6 @@ module ActiveRecord self.class.type_cast_config_to_boolean(@config.fetch(:strict, true)) end - def valid_type?(type) - !native_database_types[type].nil? - end - def default_index_type?(index) # :nodoc: index.using == :btree || super end @@ -708,7 +700,7 @@ module ActiveRecord end def extract_precision(sql_type) - if /time/.match?(sql_type) + if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type) super || 0 else super @@ -785,11 +777,11 @@ module ActiveRecord def change_column_sql(table_name, column_name, type, options = {}) column = column_for(table_name, column_name) - unless options_include_default?(options) + unless options.key?(:default) options[:default] = column.default end - unless options.has_key?(:null) + unless options.key?(:null) options[:null] = column.null end @@ -869,9 +861,9 @@ module ActiveRecord variables["sql_auto_is_null"] = 0 # Increase timeout so the server doesn't disconnect us. - wait_timeout = @config[:wait_timeout] + wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout]) wait_timeout = 2147483 unless wait_timeout.is_a?(Integer) - variables["wait_timeout"] = self.class.type_cast_config_to_integer(wait_timeout) + variables["wait_timeout"] = wait_timeout defaults = [":default", :default].to_set diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb index e8358271ab..083cd6340f 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb @@ -25,6 +25,14 @@ module ActiveRecord end def add_column_options!(sql, options) + # By default, TIMESTAMP columns are NOT NULL, cannot contain NULL values, + # and assigning NULL assigns the current timestamp. To permit a TIMESTAMP + # column to contain NULL, explicitly declare it with the NULL attribute. + # See http://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html + if /\Atimestamp\b/.match?(options[:column].sql_type) && !options[:primary_key] + sql << " NULL" unless options[:null] == false || options_include_default?(options) + end + if charset = options[:charset] sql << " CHARACTER SET #{charset}" end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb index 773bbcef4e..6d88c14d50 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb @@ -75,6 +75,11 @@ module ActiveRecord super end + + private + def aliased_types(name, fallback) + fallback + end end class Table < ActiveRecord::ConnectionAdapters::Table diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb index ad4a069d73..3e0afd9761 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb @@ -30,7 +30,10 @@ module ActiveRecord end def schema_type(column) - if column.sql_type == "tinyblob" + case column.sql_type + when /\Atimestamp\b/ + :timestamp + when "tinyblob" :blob else super @@ -38,7 +41,7 @@ module ActiveRecord end def schema_precision(column) - super unless /time/.match?(column.sql_type) && column.precision == 0 + super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0 end def schema_collation(column) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index a61d920a73..afef0da5c7 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -489,7 +489,7 @@ module ActiveRecord end execute sql - change_column_default(table_name, column_name, options[:default]) if options_include_default?(options) + change_column_default(table_name, column_name, options[:default]) if options.key?(:default) change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null) change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment) end @@ -615,10 +615,6 @@ module ActiveRecord end end - def index_name_length - 63 - end - # Maps logical Rails types to PostgreSQL-specific data types. def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc: sql = \ diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index c89e29ba44..bc04565434 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -215,7 +215,7 @@ module ActiveRecord # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil - @table_alias_length = nil + @max_identifier_length = nil connect add_pg_encoders @@ -281,11 +281,6 @@ module ActiveRecord NATIVE_DATABASE_TYPES end - # Returns true, since this connection adapter supports migrations. - def supports_migrations? - true - end - def set_standard_conforming_strings execute("SET standard_conforming_strings = on", "SCHEMA") end @@ -363,8 +358,9 @@ module ActiveRecord # Returns the configured supported identifier length supported by PostgreSQL def table_alias_length - @table_alias_length ||= query("SHOW max_identifier_length", "SCHEMA")[0][0].to_i + @max_identifier_length ||= select_value("SHOW max_identifier_length", "SCHEMA").to_i end + alias index_name_length table_alias_length # Set the authorized user for this session def session_auth=(user) @@ -376,10 +372,6 @@ module ActiveRecord @use_insert_returning end - def valid_type?(type) - !native_database_types[type].nil? - end - def update_table_definition(table_name, base) #:nodoc: PostgreSQL::Table.new(table_name, base) end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 16ef195bfc..285b0ec243 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -117,11 +117,6 @@ module ActiveRecord true end - # Returns true, since this connection adapter supports migrations. - def supports_migrations? #:nodoc: - true - end - def requires_reloading? true end @@ -163,7 +158,7 @@ module ActiveRecord true end - def valid_type?(type) + def valid_type?(type) # :nodoc: true end @@ -420,11 +415,10 @@ module ActiveRecord def change_column(table_name, column_name, type, options = {}) #:nodoc: alter_table(table_name) do |definition| - include_default = options_include_default?(options) definition[column_name].instance_eval do self.type = type self.limit = options[:limit] if options.include?(:limit) - self.default = options[:default] if include_default + self.default = options[:default] if options.include?(:default) self.null = options[:null] if options.include?(:null) self.precision = options[:precision] if options.include?(:precision) self.scale = options[:scale] if options.include?(:scale) diff --git a/activerecord/lib/active_record/gem_version.rb b/activerecord/lib/active_record/gem_version.rb index f33456a744..174f716152 100644 --- a/activerecord/lib/active_record/gem_version.rb +++ b/activerecord/lib/active_record/gem_version.rb @@ -8,7 +8,7 @@ module ActiveRecord MAJOR = 5 MINOR = 1 TINY = 0 - PRE = "alpha" + PRE = "beta1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 40f6226315..263a8a7da3 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -548,12 +548,10 @@ module ActiveRecord end def call(env) - if connection.supports_migrations? - mtime = ActiveRecord::Migrator.last_migration.mtime.to_i - if @last_check < mtime - ActiveRecord::Migration.check_pending!(connection) - @last_check = mtime - end + mtime = ActiveRecord::Migrator.last_migration.mtime.to_i + if @last_check < mtime + ActiveRecord::Migration.check_pending!(connection) + @last_check = mtime end @app.call(env) end @@ -1098,8 +1096,6 @@ module ActiveRecord end def initialize(direction, migrations, target_version = nil) - raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations? - @direction = direction @target_version = target_version @migrated_versions = nil diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 246d330b76..fabd326649 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -288,8 +288,7 @@ db_namespace = namespace :db do current_config = ActiveRecord::Tasks::DatabaseTasks.current_config ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename) - if ActiveRecord::Base.connection.supports_migrations? && - ActiveRecord::SchemaMigration.table_exists? + if ActiveRecord::SchemaMigration.table_exists? File.open(filename, "a") do |f| f.puts ActiveRecord::Base.connection.dump_schema_information f.print "\n" diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 35c670f1a1..f4cdaf3948 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -232,7 +232,7 @@ module ActiveRecord query_builder = build_count_subquery(spawn, column_name, distinct) else # PostgreSQL doesn't like ORDER BY when there are no GROUP BY - relation = unscope(:order) + relation = unscope(:order).distinct!(false) column = aggregate_column(column_name) @@ -282,7 +282,7 @@ module ActiveRecord operation, distinct).as(aggregate_alias) ] - select_values += select_values unless having_clause.empty? + select_values += self.select_values unless having_clause.empty? select_values.concat group_columns.map { |aliaz, field| if field.respond_to?(:as) @@ -292,7 +292,7 @@ module ActiveRecord end } - relation = except(:group) + relation = except(:group).distinct!(false) relation.group_values = group_fields relation.select_values = select_values diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 4548944fe6..5d24f5f5ca 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -147,7 +147,7 @@ module ActiveRecord def last(limit = nil) return find_last(limit) if loaded? || limit_value - result = limit(limit || 1) + result = limit(limit) result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key result = result.reverse_order! @@ -430,140 +430,142 @@ module ActiveRecord reflections.none?(&:collection?) end - private + def find_with_ids(*ids) + raise UnknownPrimaryKey.new(@klass) if primary_key.nil? - def find_with_ids(*ids) - raise UnknownPrimaryKey.new(@klass) if primary_key.nil? + expects_array = ids.first.kind_of?(Array) + return ids.first if expects_array && ids.first.empty? - expects_array = ids.first.kind_of?(Array) - return ids.first if expects_array && ids.first.empty? + ids = ids.flatten.compact.uniq - ids = ids.flatten.compact.uniq - - case ids.size - when 0 - raise RecordNotFound, "Couldn't find #{@klass.name} without an ID" - when 1 - result = find_one(ids.first) - expects_array ? [ result ] : result - else - find_some(ids) - end - rescue ::RangeError - raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID" + case ids.size + when 0 + raise RecordNotFound, "Couldn't find #{@klass.name} without an ID" + when 1 + result = find_one(ids.first) + expects_array ? [ result ] : result + else + find_some(ids) end + rescue ::RangeError + raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID" + end - def find_one(id) - if ActiveRecord::Base === id - raise ArgumentError, <<-MSG.squish - You are passing an instance of ActiveRecord::Base to `find`. - Please pass the id of the object by calling `.id`. - MSG - end - - relation = where(primary_key => id) - record = relation.take - - raise_record_not_found_exception!(id, 0, 1) unless record - - record + def find_one(id) + if ActiveRecord::Base === id + raise ArgumentError, <<-MSG.squish + You are passing an instance of ActiveRecord::Base to `find`. + Please pass the id of the object by calling `.id`. + MSG end - def find_some(ids) - return find_some_ordered(ids) unless order_values.present? + relation = where(primary_key => id) + record = relation.take - result = where(primary_key => ids).to_a + raise_record_not_found_exception!(id, 0, 1) unless record - expected_size = - if limit_value && ids.size > limit_value - limit_value - else - ids.size - end + record + end - # 11 ids with limit 3, offset 9 should give 2 results. - if offset_value && (ids.size - offset_value < expected_size) - expected_size = ids.size - offset_value - end + def find_some(ids) + return find_some_ordered(ids) unless order_values.present? - if result.size == expected_size - result + result = where(primary_key => ids).to_a + + expected_size = + if limit_value && ids.size > limit_value + limit_value else - raise_record_not_found_exception!(ids, result.size, expected_size) + ids.size end + + # 11 ids with limit 3, offset 9 should give 2 results. + if offset_value && (ids.size - offset_value < expected_size) + expected_size = ids.size - offset_value end - def find_some_ordered(ids) - ids = ids.slice(offset_value || 0, limit_value || ids.size) || [] + if result.size == expected_size + result + else + raise_record_not_found_exception!(ids, result.size, expected_size) + end + end - result = except(:limit, :offset).where(primary_key => ids).records + def find_some_ordered(ids) + ids = ids.slice(offset_value || 0, limit_value || ids.size) || [] - if result.size == ids.size - pk_type = @klass.type_for_attribute(primary_key) + result = except(:limit, :offset).where(primary_key => ids).records - records_by_id = result.index_by(&:id) - ids.map { |id| records_by_id.fetch(pk_type.cast(id)) } - else - raise_record_not_found_exception!(ids, result.size, ids.size) - end - end + if result.size == ids.size + pk_type = @klass.type_for_attribute(primary_key) - def find_take - if loaded? - records.first - else - @take ||= limit(1).records.first - end + records_by_id = result.index_by(&:id) + ids.map { |id| records_by_id.fetch(pk_type.cast(id)) } + else + raise_record_not_found_exception!(ids, result.size, ids.size) end + end - def find_take_with_limit(limit) - if loaded? - records.take(limit) - else - limit(limit).to_a - end + def find_take + if loaded? + records.first + else + @take ||= limit(1).records.first end + end - def find_nth(index) - @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first + def find_take_with_limit(limit) + if loaded? + records.take(limit) + else + limit(limit).to_a end + end + + def find_nth(index) + @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first + end - def find_nth_with_limit(index, limit) - if loaded? - records[index, limit] || [] + def find_nth_with_limit(index, limit) + if loaded? + records[index, limit] || [] + else + relation = if order_values.empty? && primary_key + order(arel_attribute(primary_key).asc) else - relation = if order_values.empty? && primary_key - order(arel_attribute(primary_key).asc) - else - self - end + self + end + if limit_value.nil? || index < limit_value relation = relation.offset(offset_index + index) unless index.zero? relation.limit(limit).to_a + else + [] end end + end - def find_nth_from_last(index) - if loaded? - records[-index] + def find_nth_from_last(index) + if loaded? + records[-index] + else + relation = if order_values.empty? && primary_key + order(arel_attribute(primary_key).asc) else - relation = if order_values.empty? && primary_key - order(arel_attribute(primary_key).asc) - else - self - end - - relation.to_a[-index] - # TODO: can be made more performant on large result sets by - # for instance, last(index)[-index] (which would require - # refactoring the last(n) finder method to make test suite pass), - # or by using a combination of reverse_order, limit, and offset, - # e.g., reverse_order.offset(index-1).first + self end - end - def find_last(limit) - limit ? records.last(limit) : records.last + relation.to_a[-index] + # TODO: can be made more performant on large result sets by + # for instance, last(index)[-index] (which would require + # refactoring the last(n) finder method to make test suite pass), + # or by using a combination of reverse_order, limit, and offset, + # e.g., reverse_order.offset(index-1).first end + end + + def find_last(limit) + limit ? records.last(limit) : records.last + end end end diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 9ed70a9c2b..26b1d48e9e 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -41,10 +41,15 @@ module ActiveRecord @column_types = column_types end + # Returns the number of elements in the rows array. def length @rows.length end + # Calls the given block once for each element in row collection, passing + # row as parameter. + # + # Returns an +Enumerator+ if no block is given. def each if block_given? hash_rows.each { |row| yield row } @@ -53,6 +58,7 @@ module ActiveRecord end end + # Returns an array of hashes representing each row record. def to_hash hash_rows end @@ -60,11 +66,12 @@ module ActiveRecord alias :map! :map alias :collect! :map - # Returns true if there are no records. + # Returns true if there are no records, otherwise false. def empty? rows.empty? end + # Returns an array of hashes representing each row record. def to_ary hash_rows end @@ -73,11 +80,15 @@ module ActiveRecord hash_rows[idx] end + # Returns the first record from the rows collection. + # If the rows collection is empty, returns +nil+. def first return nil if @rows.empty? Hash[@columns.zip(@rows.first)] end + # Returns the last record from the rows collection. + # If the rows collection is empty, returns +nil+. def last return nil if @rows.empty? Hash[@columns.zip(@rows.last)] diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 427c0019c6..64bda1539c 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -1,4 +1,3 @@ - module ActiveRecord module Sanitization extend ActiveSupport::Concern @@ -207,9 +206,9 @@ module ActiveRecord end end - # TODO: Deprecate this def quoted_id # :nodoc: self.class.connection.quote(@attributes[self.class.primary_key].value_for_database) end + deprecate :quoted_id end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 15533f0151..2bbfd01698 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -85,7 +85,7 @@ HEADER end def tables(stream) - sorted_tables = @connection.data_sources.sort - @connection.views + sorted_tables = @connection.tables.sort sorted_tables.each do |table_name| table(table_name, stream) unless ignored?(table_name) diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 08417aaa0f..690deee508 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -283,7 +283,7 @@ module ActiveRecord fire_on = Array(options[:on]) assert_valid_transaction_action(fire_on) options[:if] = Array(options[:if]) - options[:if] << "transaction_include_any_action?(#{fire_on})" + options[:if].unshift("transaction_include_any_action?(#{fire_on})") end end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 1f94472390..a2faf43b0d 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -42,7 +42,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase @connection.update("set @@wait_timeout=1") sleep 2 assert !@connection.active? - + ensure # Repair all fixture connections so other tests won't break. @fixture_connections.each(&:verify!) end @@ -63,6 +63,18 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase assert @connection.active? end + def test_verify_with_args_is_deprecated + assert_deprecated do + @connection.verify!(option: true) + end + assert_deprecated do + @connection.verify!([]) + end + assert_deprecated do + @connection.verify!({}) + end + end + def test_execute_after_disconnect @connection.disconnect! @@ -85,6 +97,22 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase assert_equal false, @connection.active? end + def test_wait_timeout_as_string + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge(wait_timeout: "60")) + result = ActiveRecord::Base.connection.select_value("SELECT @@SESSION.wait_timeout") + assert_equal 60, result + end + end + + def test_wait_timeout_as_url + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge("url" => "mysql2:///?wait_timeout=60")) + result = ActiveRecord::Base.connection.select_value("SELECT @@SESSION.wait_timeout") + assert_equal 60, result + end + end + def test_mysql_connection_collation_is_configured assert_equal "utf8_unicode_ci", @connection.show_variable("collation_connection") assert_equal "utf8_general_ci", ARUnit2Model.connection.show_variable("collation_connection") diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 3cbd4ca212..c52d9e37cc 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -105,7 +105,7 @@ module ActiveRecord end def test_table_alias_length_logs_name - @connection.instance_variable_set("@table_alias_length", nil) + @connection.instance_variable_set("@max_identifier_length", nil) @connection.table_alias_length assert_equal "SCHEMA", @subscriber.logged[0][1] end @@ -177,7 +177,7 @@ module ActiveRecord assert_not_equal original_connection_pid, new_connection_pid, "umm -- looks like you didn't break the connection, because we're still " \ "successfully querying with the same connection pid." - + ensure # Repair all fixture connections so other tests won't break. @fixture_connections.each(&:verify!) end diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb index 141baffa5b..a1e966b915 100644 --- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb +++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb @@ -1,5 +1,4 @@ require "cases/helper" -require "ipaddr" module ActiveRecord module ConnectionAdapters diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb index 9750840051..aefbb309e6 100644 --- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb @@ -1,6 +1,5 @@ require "cases/helper" require "bigdecimal" -require "yaml" require "securerandom" class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase @@ -15,31 +14,6 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase assert_equal expected, @conn.type_cast(binary) end - def test_type_cast_symbol - assert_equal "foo", @conn.type_cast(:foo) - end - - def test_type_cast_date - date = Date.today - expected = @conn.quoted_date(date) - assert_equal expected, @conn.type_cast(date) - end - - def test_type_cast_time - time = Time.now - expected = @conn.quoted_date(time) - assert_equal expected, @conn.type_cast(time) - end - - def test_type_cast_numeric - assert_equal 10, @conn.type_cast(10) - assert_equal 2.2, @conn.type_cast(2.2) - end - - def test_type_cast_nil - assert_nil @conn.type_cast(nil) - end - def test_type_cast_true assert_equal "t", @conn.type_cast(true) end @@ -53,31 +27,6 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase assert_equal bd.to_f, @conn.type_cast(bd) end - def test_type_cast_unknown_should_raise_error - obj = Class.new.new - assert_raise(TypeError) { @conn.type_cast(obj) } - end - - def test_type_cast_object_which_responds_to_quoted_id - quoted_id_obj = Class.new { - def quoted_id - "'zomg'" - end - - def id - 10 - end - }.new - assert_equal 10, @conn.type_cast(quoted_id_obj) - - quoted_id_obj = Class.new { - def quoted_id - "'zomg'" - end - }.new - assert_raise(TypeError) { @conn.type_cast(quoted_id_obj) } - end - def test_quoting_binary_strings value = "hello".encode("ascii-8bit") type = ActiveRecord::Type::String.new diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 397ac599b9..5b608d8e83 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -1,146 +1,143 @@ require "cases/helper" -if ActiveRecord::Base.connection.supports_migrations? +class ActiveRecordSchemaTest < ActiveRecord::TestCase + self.use_transactional_tests = false + + setup do + @original_verbose = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + @connection = ActiveRecord::Base.connection + ActiveRecord::SchemaMigration.drop_table + end - class ActiveRecordSchemaTest < ActiveRecord::TestCase - self.use_transactional_tests = false + teardown do + @connection.drop_table :fruits rescue nil + @connection.drop_table :nep_fruits rescue nil + @connection.drop_table :nep_schema_migrations rescue nil + @connection.drop_table :has_timestamps rescue nil + @connection.drop_table :multiple_indexes rescue nil + ActiveRecord::SchemaMigration.delete_all rescue nil + ActiveRecord::Migration.verbose = @original_verbose + end - setup do - @original_verbose = ActiveRecord::Migration.verbose - ActiveRecord::Migration.verbose = false - @connection = ActiveRecord::Base.connection - ActiveRecord::SchemaMigration.drop_table - end + def test_has_primary_key + old_primary_key_prefix_type = ActiveRecord::Base.primary_key_prefix_type + ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore + assert_equal "version", ActiveRecord::SchemaMigration.primary_key - teardown do - @connection.drop_table :fruits rescue nil - @connection.drop_table :nep_fruits rescue nil - @connection.drop_table :nep_schema_migrations rescue nil - @connection.drop_table :has_timestamps rescue nil - @connection.drop_table :multiple_indexes rescue nil - ActiveRecord::SchemaMigration.delete_all rescue nil - ActiveRecord::Migration.verbose = @original_verbose + ActiveRecord::SchemaMigration.create_table + assert_difference "ActiveRecord::SchemaMigration.count", 1 do + ActiveRecord::SchemaMigration.create version: 12 end + ensure + ActiveRecord::SchemaMigration.drop_table + ActiveRecord::Base.primary_key_prefix_type = old_primary_key_prefix_type + end - def test_has_primary_key - old_primary_key_prefix_type = ActiveRecord::Base.primary_key_prefix_type - ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore - assert_equal "version", ActiveRecord::SchemaMigration.primary_key - - ActiveRecord::SchemaMigration.create_table - assert_difference "ActiveRecord::SchemaMigration.count", 1 do - ActiveRecord::SchemaMigration.create version: 12 + def test_schema_define + ActiveRecord::Schema.define(version: 7) do + create_table :fruits do |t| + t.column :color, :string + t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle + t.column :texture, :string + t.column :flavor, :string end - ensure - ActiveRecord::SchemaMigration.drop_table - ActiveRecord::Base.primary_key_prefix_type = old_primary_key_prefix_type end - def test_schema_define - ActiveRecord::Schema.define(version: 7) do - create_table :fruits do |t| - t.column :color, :string - t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle - t.column :texture, :string - t.column :flavor, :string - end - end - - assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" } - assert_nothing_raised { @connection.select_all "SELECT * FROM schema_migrations" } - assert_equal 7, ActiveRecord::Migrator::current_version - end + assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" } + assert_nothing_raised { @connection.select_all "SELECT * FROM schema_migrations" } + assert_equal 7, ActiveRecord::Migrator::current_version + end - def test_schema_define_w_table_name_prefix - table_name = ActiveRecord::SchemaMigration.table_name - old_table_name_prefix = ActiveRecord::Base.table_name_prefix - ActiveRecord::Base.table_name_prefix = "nep_" - ActiveRecord::SchemaMigration.table_name = "nep_#{table_name}" - ActiveRecord::Schema.define(version: 7) do - create_table :fruits do |t| - t.column :color, :string - t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle - t.column :texture, :string - t.column :flavor, :string - end + def test_schema_define_w_table_name_prefix + table_name = ActiveRecord::SchemaMigration.table_name + old_table_name_prefix = ActiveRecord::Base.table_name_prefix + ActiveRecord::Base.table_name_prefix = "nep_" + ActiveRecord::SchemaMigration.table_name = "nep_#{table_name}" + ActiveRecord::Schema.define(version: 7) do + create_table :fruits do |t| + t.column :color, :string + t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle + t.column :texture, :string + t.column :flavor, :string end - assert_equal 7, ActiveRecord::Migrator::current_version - ensure - ActiveRecord::Base.table_name_prefix = old_table_name_prefix - ActiveRecord::SchemaMigration.table_name = table_name end + assert_equal 7, ActiveRecord::Migrator::current_version + ensure + ActiveRecord::Base.table_name_prefix = old_table_name_prefix + ActiveRecord::SchemaMigration.table_name = table_name + end - def test_schema_raises_an_error_for_invalid_column_type - assert_raise NoMethodError do - ActiveRecord::Schema.define(version: 8) do - create_table :vegetables do |t| - t.unknown :color - end + def test_schema_raises_an_error_for_invalid_column_type + assert_raise NoMethodError do + ActiveRecord::Schema.define(version: 8) do + create_table :vegetables do |t| + t.unknown :color end end end + end - def test_schema_subclass - Class.new(ActiveRecord::Schema).define(version: 9) do - create_table :fruits - end - assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" } + def test_schema_subclass + Class.new(ActiveRecord::Schema).define(version: 9) do + create_table :fruits end + assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" } + end - def test_normalize_version - assert_equal "118", ActiveRecord::SchemaMigration.normalize_migration_number("0000118") - assert_equal "002", ActiveRecord::SchemaMigration.normalize_migration_number("2") - assert_equal "017", ActiveRecord::SchemaMigration.normalize_migration_number("0017") - assert_equal "20131219224947", ActiveRecord::SchemaMigration.normalize_migration_number("20131219224947") - end + def test_normalize_version + assert_equal "118", ActiveRecord::SchemaMigration.normalize_migration_number("0000118") + assert_equal "002", ActiveRecord::SchemaMigration.normalize_migration_number("2") + assert_equal "017", ActiveRecord::SchemaMigration.normalize_migration_number("0017") + assert_equal "20131219224947", ActiveRecord::SchemaMigration.normalize_migration_number("20131219224947") + end - def test_schema_load_with_multiple_indexes_for_column_of_different_names - ActiveRecord::Schema.define do - create_table :multiple_indexes do |t| - t.string "foo" - t.index ["foo"], name: "multiple_indexes_foo_1" - t.index ["foo"], name: "multiple_indexes_foo_2" - end + def test_schema_load_with_multiple_indexes_for_column_of_different_names + ActiveRecord::Schema.define do + create_table :multiple_indexes do |t| + t.string "foo" + t.index ["foo"], name: "multiple_indexes_foo_1" + t.index ["foo"], name: "multiple_indexes_foo_2" end + end - indexes = @connection.indexes("multiple_indexes") + indexes = @connection.indexes("multiple_indexes") - assert_equal 2, indexes.length - assert_equal ["multiple_indexes_foo_1", "multiple_indexes_foo_2"], indexes.collect(&:name).sort - end + assert_equal 2, indexes.length + assert_equal ["multiple_indexes_foo_1", "multiple_indexes_foo_2"], indexes.collect(&:name).sort + end - def test_timestamps_without_null_set_null_to_false_on_create_table - ActiveRecord::Schema.define do - create_table :has_timestamps do |t| - t.timestamps - end + def test_timestamps_without_null_set_null_to_false_on_create_table + ActiveRecord::Schema.define do + create_table :has_timestamps do |t| + t.timestamps end - - assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null end - def test_timestamps_without_null_set_null_to_false_on_change_table - ActiveRecord::Schema.define do - create_table :has_timestamps + assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null + assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + end - change_table :has_timestamps do |t| - t.timestamps default: Time.now - end - end + def test_timestamps_without_null_set_null_to_false_on_change_table + ActiveRecord::Schema.define do + create_table :has_timestamps - assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + change_table :has_timestamps do |t| + t.timestamps default: Time.now + end end - def test_timestamps_without_null_set_null_to_false_on_add_timestamps - ActiveRecord::Schema.define do - create_table :has_timestamps - add_timestamps :has_timestamps, default: Time.now - end + assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null + assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + end - assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + def test_timestamps_without_null_set_null_to_false_on_add_timestamps + ActiveRecord::Schema.define do + create_table :has_timestamps + add_timestamps :has_timestamps, default: Time.now end + + assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null + assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null end end diff --git a/activerecord/test/cases/associations/eager_singularization_test.rb b/activerecord/test/cases/associations/eager_singularization_test.rb index 5d1c1c4b9b..16eff15026 100644 --- a/activerecord/test/cases/associations/eager_singularization_test.rb +++ b/activerecord/test/cases/associations/eager_singularization_test.rb @@ -1,147 +1,146 @@ require "cases/helper" -if ActiveRecord::Base.connection.supports_migrations? - class EagerSingularizationTest < ActiveRecord::TestCase - class Virus < ActiveRecord::Base - belongs_to :octopus - end - - class Octopus < ActiveRecord::Base - has_one :virus - end - - class Pass < ActiveRecord::Base - belongs_to :bus - end - - class Bus < ActiveRecord::Base - has_many :passes - end - - class Mess < ActiveRecord::Base - has_and_belongs_to_many :crises - end - - class Crisis < ActiveRecord::Base - has_and_belongs_to_many :messes - has_many :analyses, dependent: :destroy - has_many :successes, through: :analyses - has_many :dresses, dependent: :destroy - has_many :compresses, through: :dresses - end - - class Analysis < ActiveRecord::Base - belongs_to :crisis - belongs_to :success - end - - class Success < ActiveRecord::Base - has_many :analyses, dependent: :destroy - has_many :crises, through: :analyses - end - - class Dress < ActiveRecord::Base - belongs_to :crisis - has_many :compresses - end - - class Compress < ActiveRecord::Base - belongs_to :dress - end - - def setup - connection.create_table :viri do |t| - t.column :octopus_id, :integer - t.column :species, :string - end - connection.create_table :octopi do |t| - t.column :species, :string - end - connection.create_table :passes do |t| - t.column :bus_id, :integer - t.column :rides, :integer - end - connection.create_table :buses do |t| - t.column :name, :string - end - connection.create_table :crises_messes, id: false do |t| - t.column :crisis_id, :integer - t.column :mess_id, :integer - end - connection.create_table :messes do |t| - t.column :name, :string - end - connection.create_table :crises do |t| - t.column :name, :string - end - connection.create_table :successes do |t| - t.column :name, :string - end - connection.create_table :analyses do |t| - t.column :crisis_id, :integer - t.column :success_id, :integer - end - connection.create_table :dresses do |t| - t.column :crisis_id, :integer - end - connection.create_table :compresses do |t| - t.column :dress_id, :integer - end - end - - teardown do - connection.drop_table :viri - connection.drop_table :octopi - connection.drop_table :passes - connection.drop_table :buses - connection.drop_table :crises_messes - connection.drop_table :messes - connection.drop_table :crises - connection.drop_table :successes - connection.drop_table :analyses - connection.drop_table :dresses - connection.drop_table :compresses - end +class EagerSingularizationTest < ActiveRecord::TestCase + class Virus < ActiveRecord::Base + belongs_to :octopus + end - def connection - ActiveRecord::Base.connection + class Octopus < ActiveRecord::Base + has_one :virus + end + + class Pass < ActiveRecord::Base + belongs_to :bus + end + + class Bus < ActiveRecord::Base + has_many :passes + end + + class Mess < ActiveRecord::Base + has_and_belongs_to_many :crises + end + + class Crisis < ActiveRecord::Base + has_and_belongs_to_many :messes + has_many :analyses, dependent: :destroy + has_many :successes, through: :analyses + has_many :dresses, dependent: :destroy + has_many :compresses, through: :dresses + end + + class Analysis < ActiveRecord::Base + belongs_to :crisis + belongs_to :success + end + + class Success < ActiveRecord::Base + has_many :analyses, dependent: :destroy + has_many :crises, through: :analyses + end + + class Dress < ActiveRecord::Base + belongs_to :crisis + has_many :compresses + end + + class Compress < ActiveRecord::Base + belongs_to :dress + end + + def setup + connection.create_table :viri do |t| + t.column :octopus_id, :integer + t.column :species, :string end + connection.create_table :octopi do |t| + t.column :species, :string + end + connection.create_table :passes do |t| + t.column :bus_id, :integer + t.column :rides, :integer + end + connection.create_table :buses do |t| + t.column :name, :string + end + connection.create_table :crises_messes, id: false do |t| + t.column :crisis_id, :integer + t.column :mess_id, :integer + end + connection.create_table :messes do |t| + t.column :name, :string + end + connection.create_table :crises do |t| + t.column :name, :string + end + connection.create_table :successes do |t| + t.column :name, :string + end + connection.create_table :analyses do |t| + t.column :crisis_id, :integer + t.column :success_id, :integer + end + connection.create_table :dresses do |t| + t.column :crisis_id, :integer + end + connection.create_table :compresses do |t| + t.column :dress_id, :integer + end + end - def test_eager_no_extra_singularization_belongs_to - assert_nothing_raised do - Virus.all.merge!(includes: :octopus).to_a - end + teardown do + connection.drop_table :viri + connection.drop_table :octopi + connection.drop_table :passes + connection.drop_table :buses + connection.drop_table :crises_messes + connection.drop_table :messes + connection.drop_table :crises + connection.drop_table :successes + connection.drop_table :analyses + connection.drop_table :dresses + connection.drop_table :compresses + end + + def test_eager_no_extra_singularization_belongs_to + assert_nothing_raised do + Virus.all.merge!(includes: :octopus).to_a end + end - def test_eager_no_extra_singularization_has_one - assert_nothing_raised do - Octopus.all.merge!(includes: :virus).to_a - end + def test_eager_no_extra_singularization_has_one + assert_nothing_raised do + Octopus.all.merge!(includes: :virus).to_a end + end - def test_eager_no_extra_singularization_has_many - assert_nothing_raised do - Bus.all.merge!(includes: :passes).to_a - end + def test_eager_no_extra_singularization_has_many + assert_nothing_raised do + Bus.all.merge!(includes: :passes).to_a end + end - def test_eager_no_extra_singularization_has_and_belongs_to_many - assert_nothing_raised do - Crisis.all.merge!(includes: :messes).to_a - Mess.all.merge!(includes: :crises).to_a - end + def test_eager_no_extra_singularization_has_and_belongs_to_many + assert_nothing_raised do + Crisis.all.merge!(includes: :messes).to_a + Mess.all.merge!(includes: :crises).to_a end + end - def test_eager_no_extra_singularization_has_many_through_belongs_to - assert_nothing_raised do - Crisis.all.merge!(includes: :successes).to_a - end + def test_eager_no_extra_singularization_has_many_through_belongs_to + assert_nothing_raised do + Crisis.all.merge!(includes: :successes).to_a end + end - def test_eager_no_extra_singularization_has_many_through_has_many - assert_nothing_raised do - Crisis.all.merge!(includes: :compresses).to_a - end + def test_eager_no_extra_singularization_has_many_through_has_many + assert_nothing_raised do + Crisis.all.merge!(includes: :compresses).to_a end end + + private + def connection + ActiveRecord::Base.connection + end end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 1813534b62..f018156cfc 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -227,6 +227,20 @@ class CalculationsTest < ActiveRecord::TestCase assert_match "credit_limit, firm_name", e.message end + def test_apply_distinct_in_count + queries = assert_sql do + Account.distinct.count + Account.group(:firm_id).distinct.count + end + + queries.each do |query| + # `table_alias_length` in `column_alias_for` would execute + # "SHOW max_identifier_length" statement in PostgreSQL adapter. + next if query == "SHOW max_identifier_length" + assert_match %r{\ASELECT(?! DISTINCT) COUNT\(DISTINCT\b}, query + end + end + def test_should_group_by_summed_field_having_condition c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit) assert_nil c[1] @@ -235,7 +249,8 @@ class CalculationsTest < ActiveRecord::TestCase end def test_should_group_by_summed_field_having_condition_from_select - c = Account.select("MIN(credit_limit) AS min_credit_limit").group(:firm_id).having("MIN(credit_limit) > 50").sum(:credit_limit) + skip if current_adapter?(:PostgreSQLAdapter, :OracleAdapter) + c = Account.select("MIN(credit_limit) AS min_credit_limit").group(:firm_id).having("min_credit_limit > 50").sum(:credit_limit) assert_nil c[1] assert_equal 60, c[2] assert_equal 53, c[9] diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index afd0ac2dd4..7e88c9cf7a 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -307,14 +307,17 @@ module ActiveRecord end end - def test_automatic_reconnect= + def test_automatic_reconnect_restores_after_disconnect pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec assert pool.automatic_reconnect assert pool.connection pool.disconnect! assert pool.connection + end + def test_automatic_reconnect_can_be_disabled + pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec pool.disconnect! pool.automatic_reconnect = false diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb index 6532efcf22..a6297673c9 100644 --- a/activerecord/test/cases/defaults_test.rb +++ b/activerecord/test/cases/defaults_test.rb @@ -100,11 +100,21 @@ if current_adapter?(:Mysql2Adapter) include SchemaDumpingHelper if ActiveRecord::Base.connection.version >= "5.6.0" - test "schema dump includes default expression" do + test "schema dump datetime includes default expression" do output = dump_table_schema("datetime_defaults") assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output end end + + test "schema dump timestamp includes default expression" do + output = dump_table_schema("timestamp_defaults") + assert_match %r/t\.timestamp\s+"modified_timestamp",\s+default: -> { "CURRENT_TIMESTAMP" }/, output + end + + test "schema dump timestamp without default expression" do + output = dump_table_schema("timestamp_defaults") + assert_match %r/t\.timestamp\s+"nullable_timestamp"$/, output + end end class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index a43c06cd6e..c13a962e3e 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -566,19 +566,17 @@ class DirtyTest < ActiveRecord::TestCase travel_back end - if ActiveRecord::Base.connection.supports_migrations? - class Testings < ActiveRecord::Base; end - def test_field_named_field - ActiveRecord::Base.connection.create_table :testings do |t| - t.string :field - end - assert_nothing_raised do - Testings.new.attributes - end - ensure - ActiveRecord::Base.connection.drop_table :testings rescue nil - ActiveRecord::Base.clear_cache! + class Testings < ActiveRecord::Base; end + def test_field_named_field + ActiveRecord::Base.connection.create_table :testings do |t| + t.string :field end + assert_nothing_raised do + Testings.new.attributes + end + ensure + ActiveRecord::Base.connection.drop_table :testings rescue nil + ActiveRecord::Base.clear_cache! end def test_datetime_attribute_can_be_updated_with_fractional_seconds diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index deec669935..89d8a8bdca 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -497,7 +497,7 @@ class FinderTest < ActiveRecord::TestCase assert_nil Topic.offset(5).second_to_last #test with limit - # assert_nil Topic.limit(1).second # TODO: currently failing + assert_nil Topic.limit(1).second assert_nil Topic.limit(1).second_to_last end @@ -526,9 +526,9 @@ class FinderTest < ActiveRecord::TestCase assert_nil Topic.offset(5).third_to_last # test with limit - # assert_nil Topic.limit(1).third # TODO: currently failing + assert_nil Topic.limit(1).third assert_nil Topic.limit(1).third_to_last - # assert_nil Topic.limit(2).third # TODO: currently failing + assert_nil Topic.limit(2).third assert_nil Topic.limit(2).third_to_last end diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index afe761cb55..51133e9495 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -104,64 +104,62 @@ class FixturesTest < ActiveRecord::TestCase assert_nil(second_row["author_email_address"]) end - if ActiveRecord::Base.connection.supports_migrations? - def test_inserts_with_pre_and_suffix - # Reset cache to make finds on the new table work - ActiveRecord::FixtureSet.reset_cache - - ActiveRecord::Base.connection.create_table :prefix_other_topics_suffix do |t| - t.column :title, :string - t.column :author_name, :string - t.column :author_email_address, :string - t.column :written_on, :datetime - t.column :bonus_time, :time - t.column :last_read, :date - t.column :content, :string - t.column :approved, :boolean, default: true - t.column :replies_count, :integer, default: 0 - t.column :parent_id, :integer - t.column :type, :string, limit: 50 - end + def test_inserts_with_pre_and_suffix + # Reset cache to make finds on the new table work + ActiveRecord::FixtureSet.reset_cache - # Store existing prefix/suffix - old_prefix = ActiveRecord::Base.table_name_prefix - old_suffix = ActiveRecord::Base.table_name_suffix + ActiveRecord::Base.connection.create_table :prefix_other_topics_suffix do |t| + t.column :title, :string + t.column :author_name, :string + t.column :author_email_address, :string + t.column :written_on, :datetime + t.column :bonus_time, :time + t.column :last_read, :date + t.column :content, :string + t.column :approved, :boolean, default: true + t.column :replies_count, :integer, default: 0 + t.column :parent_id, :integer + t.column :type, :string, limit: 50 + end - # Set a prefix/suffix we can test against - ActiveRecord::Base.table_name_prefix = "prefix_" - ActiveRecord::Base.table_name_suffix = "_suffix" + # Store existing prefix/suffix + old_prefix = ActiveRecord::Base.table_name_prefix + old_suffix = ActiveRecord::Base.table_name_suffix - other_topic_klass = Class.new(ActiveRecord::Base) do - def self.name - "OtherTopic" - end + # Set a prefix/suffix we can test against + ActiveRecord::Base.table_name_prefix = "prefix_" + ActiveRecord::Base.table_name_suffix = "_suffix" + + other_topic_klass = Class.new(ActiveRecord::Base) do + def self.name + "OtherTopic" end + end - topics = [create_fixtures("other_topics")].flatten.first + topics = [create_fixtures("other_topics")].flatten.first - # This checks for a caching problem which causes a bug in the fixtures - # class-level configuration helper. - assert_not_nil topics, "Fixture data inserted, but fixture objects not returned from create" + # This checks for a caching problem which causes a bug in the fixtures + # class-level configuration helper. + assert_not_nil topics, "Fixture data inserted, but fixture objects not returned from create" - first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'David'") - assert_not_nil first_row, "The prefix_other_topics_suffix table appears to be empty despite create_fixtures: the row with author_name = 'David' was not found" - assert_equal("The First Topic", first_row["title"]) + first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'David'") + assert_not_nil first_row, "The prefix_other_topics_suffix table appears to be empty despite create_fixtures: the row with author_name = 'David' was not found" + assert_equal("The First Topic", first_row["title"]) - second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'Mary'") - assert_nil(second_row["author_email_address"]) + second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'Mary'") + assert_nil(second_row["author_email_address"]) - assert_equal :prefix_other_topics_suffix, topics.table_name.to_sym - # This assertion should preferably be the last in the list, because calling - # other_topic_klass.table_name sets a class-level instance variable - assert_equal :prefix_other_topics_suffix, other_topic_klass.table_name.to_sym + assert_equal :prefix_other_topics_suffix, topics.table_name.to_sym + # This assertion should preferably be the last in the list, because calling + # other_topic_klass.table_name sets a class-level instance variable + assert_equal :prefix_other_topics_suffix, other_topic_klass.table_name.to_sym - ensure - # Restore prefix/suffix to its previous values - ActiveRecord::Base.table_name_prefix = old_prefix - ActiveRecord::Base.table_name_suffix = old_suffix + ensure + # Restore prefix/suffix to its previous values + ActiveRecord::Base.table_name_prefix = old_prefix + ActiveRecord::Base.table_name_suffix = old_suffix - ActiveRecord::Base.connection.drop_table :prefix_other_topics_suffix rescue nil - end + ActiveRecord::Base.connection.drop_table :prefix_other_topics_suffix rescue nil end def test_insert_with_datetime diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 48cfe89882..1d305fa11f 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -269,6 +269,8 @@ module ActiveRecord if current_adapter?(:PostgreSQLAdapter) assert_equal "timestamp without time zone", klass.columns_hash["foo"].sql_type + elsif current_adapter?(:Mysql2Adapter) + assert_equal "timestamp", klass.columns_hash["foo"].sql_type else assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type end diff --git a/activerecord/test/cases/migration/columns_test.rb b/activerecord/test/cases/migration/columns_test.rb index 55c06da411..2329888345 100644 --- a/activerecord/test/cases/migration/columns_test.rb +++ b/activerecord/test/cases/migration/columns_test.rb @@ -225,6 +225,16 @@ module ActiveRecord assert_nil TestModel.new.contributor end + def test_change_column_to_drop_default_with_null_false + add_column "test_models", "contributor", :boolean, default: true, null: false + assert TestModel.new.contributor? + + change_column "test_models", "contributor", :boolean, default: nil, null: false + TestModel.reset_column_information + assert_not TestModel.new.contributor? + assert_nil TestModel.new.contributor + end + def test_change_column_with_new_default add_column "test_models", "administrator", :boolean, default: true assert TestModel.new.administrator? diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb index 26b1bb4419..c4896f3d6e 100644 --- a/activerecord/test/cases/migration/create_join_table_test.rb +++ b/activerecord/test/cases/migration/create_join_table_test.rb @@ -12,7 +12,7 @@ module ActiveRecord teardown do %w(artists_musics musics_videos catalog).each do |table_name| - connection.drop_table table_name if connection.table_exists?(table_name) + connection.drop_table table_name, if_exists: true end end @@ -78,6 +78,17 @@ module ActiveRecord assert_equal [%w(artist_id music_id)], connection.indexes(:artists_musics).map(&:columns) end + def test_create_join_table_respects_reference_key_type + connection.create_join_table :artists, :musics do |t| + t.references :video + end + + artist_id, music_id, video_id = connection.columns(:artists_musics).sort_by(&:name) + + assert_equal video_id.sql_type, artist_id.sql_type + assert_equal video_id.sql_type, music_id.sql_type + end + def test_drop_join_table connection.create_join_table :artists, :musics connection.drop_join_table :artists, :musics diff --git a/activerecord/test/cases/migration/pending_migrations_test.rb b/activerecord/test/cases/migration/pending_migrations_test.rb index 61f5a061b0..6970fdcc87 100644 --- a/activerecord/test/cases/migration/pending_migrations_test.rb +++ b/activerecord/test/cases/migration/pending_migrations_test.rb @@ -21,8 +21,6 @@ module ActiveRecord end def test_errors_if_pending - @connection.expect :supports_migrations?, true - ActiveRecord::Migrator.stub :needs_migration?, true do assert_raise ActiveRecord::PendingMigrationError do @pending.call(nil) @@ -31,22 +29,12 @@ module ActiveRecord end def test_checks_if_supported - @connection.expect :supports_migrations?, true @app.expect :call, nil, [:foo] ActiveRecord::Migrator.stub :needs_migration?, false do @pending.call(:foo) end end - - def test_doesnt_check_if_unsupported - @connection.expect :supports_migrations?, false - @app.expect :call, nil, [:foo] - - ActiveRecord::Migrator.stub :needs_migration?, true do - @pending.call(:foo) - end - end end end end diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb index 9418995ea0..f1ddac1ee2 100644 --- a/activerecord/test/cases/migration/references_foreign_key_test.rb +++ b/activerecord/test/cases/migration/references_foreign_key_test.rb @@ -203,6 +203,22 @@ if ActiveRecord::Base.connection.supports_foreign_keys? assert_equal([["testings", "testing_parents", "parent1_id"], ["testings", "testing_parents", "parent2_id"]], fk_definitions) end + + test "multiple foreign keys can be removed to the selected one" do + @connection.create_table :testings do |t| + t.references :parent1, foreign_key: { to_table: :testing_parents } + t.references :parent2, foreign_key: { to_table: :testing_parents } + end + + assert_difference "@connection.foreign_keys('testings').size", -1 do + @connection.remove_reference :testings, :parent1, foreign_key: { to_table: :testing_parents } + end + + fks = @connection.foreign_keys("testings").sort_by(&:column) + + fk_definitions = fks.map { |fk| [fk.from_table, fk.to_table, fk.column] } + assert_equal([["testings", "testing_parents", "parent2_id"]], fk_definitions) + end end end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index de16ecf442..fabb1662a3 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1142,4 +1142,8 @@ class CopyMigrationsTest < ActiveRecord::TestCase def test_deprecate_migration_keys assert_deprecated { ActiveRecord::Base.connection.migration_keys } end + + def test_deprecate_supports_migrations + assert_deprecated { ActiveRecord::Base.connection.supports_migrations? } + end end diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 03c8644229..12386635f6 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -291,6 +291,14 @@ class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase schema = dump_table_schema "barcodes" assert_match %r{create_table "barcodes", primary_key: "code", id: :string, limit: 42}, schema end + + if current_adapter?(:Mysql2Adapter) && subsecond_precision_supported? + test "schema typed primary key column" do + @connection.create_table(:scheduled_logs, id: :timestamp, precision: 6, force: true) + schema = dump_table_schema("scheduled_logs") + assert_match %r/create_table "scheduled_logs", id: :timestamp, precision: 6/, schema + end + end end class CompositePrimaryKeyTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb index 5ff5e3c735..f260d043e4 100644 --- a/activerecord/test/cases/quoting_test.rb +++ b/activerecord/test/cases/quoting_test.rb @@ -82,7 +82,7 @@ module ActiveRecord end def test_quote_with_quoted_id - assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1)) + assert_deprecated { assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1)) } end def test_quote_nil @@ -150,6 +150,62 @@ module ActiveRecord end end + class TypeCastingTest < ActiveRecord::TestCase + def setup + @conn = ActiveRecord::Base.connection + end + + def test_type_cast_symbol + assert_equal "foo", @conn.type_cast(:foo) + end + + def test_type_cast_date + date = Date.today + expected = @conn.quoted_date(date) + assert_equal expected, @conn.type_cast(date) + end + + def test_type_cast_time + time = Time.now + expected = @conn.quoted_date(time) + assert_equal expected, @conn.type_cast(time) + end + + def test_type_cast_numeric + assert_equal 10, @conn.type_cast(10) + assert_equal 2.2, @conn.type_cast(2.2) + end + + def test_type_cast_nil + assert_nil @conn.type_cast(nil) + end + + def test_type_cast_unknown_should_raise_error + obj = Class.new.new + assert_raise(TypeError) { @conn.type_cast(obj) } + end + + def test_type_cast_object_which_responds_to_quoted_id + quoted_id_obj = Class.new { + def quoted_id + "'zomg'" + end + + def id + 10 + end + }.new + assert_equal 10, @conn.type_cast(quoted_id_obj) + + quoted_id_obj = Class.new { + def quoted_id + "'zomg'" + end + }.new + assert_raise(TypeError) { @conn.type_cast(quoted_id_obj) } + end + end + class QuoteBooleanTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection @@ -165,5 +221,32 @@ module ActiveRecord assert_predicate @connection.type_cast(false), :frozen? end end + + if subsecond_precision_supported? + class QuoteARBaseTest < ActiveRecord::TestCase + class DatetimePrimaryKey < ActiveRecord::Base + end + + def setup + @time = ::Time.utc(2017, 2, 14, 12, 34, 56, 789999) + @connection = ActiveRecord::Base.connection + @connection.create_table :datetime_primary_keys, id: :datetime, precision: 3, force: true + end + + def teardown + @connection.drop_table :datetime_primary_keys, if_exists: true + end + + def test_quote_ar_object + value = DatetimePrimaryKey.new(id: @time) + assert_equal "'2017-02-14 12:34:56.789000'", @connection.quote(value) + end + + def test_type_cast_ar_object + value = DatetimePrimaryKey.new(id: @time) + assert_equal "2017-02-14 12:34:56.789000", @connection.type_cast(value) + end + end + end end end diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb index 23bcb0af1e..72f09186e2 100644 --- a/activerecord/test/cases/sanitize_test.rb +++ b/activerecord/test/cases/sanitize_test.rb @@ -152,11 +152,15 @@ class SanitizeTest < ActiveRecord::TestCase end def test_bind_record - o = Struct.new(:quoted_id).new(1) - assert_equal "1", bind("?", o) + o = Class.new { + def quoted_id + 1 + end + }.new + assert_deprecated { assert_equal "1", bind("?", o) } os = [o] * 3 - assert_equal "1,1,1", bind("?", os) + assert_deprecated { assert_equal "1,1,1", bind("?", os) } end def test_named_bind_with_postgresql_type_casts diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index 995ff4dfc5..d261fd5321 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -161,7 +161,7 @@ class NamedScopingTest < ActiveRecord::TestCase end def test_first_and_last_should_allow_integers_for_limit - assert_equal Topic.base.first(2), Topic.base.to_a.first(2) + assert_equal Topic.base.first(2), Topic.base.order("id").to_a.first(2) assert_equal Topic.base.last(2), Topic.base.order("id").to_a.last(2) end diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index 391bbe8877..eaa4dd09a9 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -551,3 +551,43 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase assert_equal [:rollback], @topic.history end end + +class CallbacksOnActionAndConditionTest < ActiveRecord::TestCase + self.use_transactional_tests = false + + class TopicWithCallbacksOnActionAndCondition < ActiveRecord::Base + self.table_name = :topics + + after_commit(on: [:create, :update], if: :run_callback?) { |record| record.history << :create_or_update } + + def clear_history + @history = [] + end + + def history + @history ||= [] + end + + def run_callback? + self.history << :run_callback? + true + end + + attr_accessor :save_before_commit_history, :update_title + end + + def test_callback_on_action_with_condition + topic = TopicWithCallbacksOnActionAndCondition.new + topic.save + assert_equal [:run_callback?, :create_or_update], topic.history + + topic.clear_history + topic.approved = true + topic.save + assert_equal [:run_callback?, :create_or_update], topic.history + + topic.clear_history + topic.destroy + assert_equal [], topic.history + end +end diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb index 9a203a7293..90a314c83c 100644 --- a/activerecord/test/schema/mysql2_specific_schema.rb +++ b/activerecord/test/schema/mysql2_specific_schema.rb @@ -6,6 +6,11 @@ ActiveRecord::Schema.define do end end + create_table :timestamp_defaults, force: true do |t| + t.timestamp :nullable_timestamp + t.timestamp :modified_timestamp, default: -> { "CURRENT_TIMESTAMP" } + end + create_table :binary_fields, force: true do |t| t.binary :var_binary, limit: 255 t.binary :var_binary_large, limit: 4095 diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb index bc5af36a28..1a609e13c3 100644 --- a/activerecord/test/support/connection.rb +++ b/activerecord/test/support/connection.rb @@ -10,7 +10,10 @@ module ARTest end def self.connection_config - config["connections"][connection_name] + config.fetch("connections").fetch(connection_name) do + puts "Connection #{connection_name.inspect} not found. Available connections: #{config['connections'].keys.join(', ')}" + exit 1 + end end def self.connect |