diff options
Diffstat (limited to 'activerecord')
150 files changed, 1373 insertions, 454 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 5b09101bca..99c78ad934 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,108 @@ +* Let `WITH` queries (Common Table Expressions) be explainable. + + *Vladimir Kochnev* + +* Make `remove_index :table, :column` reversible. + + *Yves Senn* + +* Fixed an error which would occur in dirty checking when calling + `update_attributes` from a getter. + + Fixes #20531. + + *Sean Griffin* + +* Make `remove_foreign_key` reversible. Any foreign key options must be + specified, similar to `remove_column`. + + *Aster Ryan* + +* Add `:enum_prefix`/`:enum_suffix` option to `enum` definition. + + Fixes #17511, #17415. + + *Igor Kapkov* + +* Correctly handle decimal arrays with defaults in the schema dumper. + + Fixes #20515. + + *Sean Griffin & jmondo* + +* Deprecate the PostgreSQL `:point` type in favor of a new one which will return + `Point` objects instead of an `Array` + + *Sean Griffin* + +* Ensure symbols passed to `ActiveRecord::Relation#select` are always treated + as columns. + + Fixes #20360. + + *Sean Griffin* + +* Do not set `sql_mode` if `strict: :default` is specified. + + ``` + # database.yml + production: + adapter: mysql2 + database: foo_prod + user: foo + strict: :default + ``` + + *Ryuta Kamizono* + +* Allow proc defaults to be passed to the attributes API. See documentation + for examples. + + *Sean Griffin*, *Kir Shatrov* + +* SQLite: `:collation` support for string and text columns. + + Example: + + create_table :foo do |t| + t.string :string_nocase, collation: 'NOCASE' + t.text :text_rtrim, collation: 'RTRIM' + end + + add_column :foo, :title, :string, collation: 'RTRIM' + + change_column :foo, :title, :string, collation: 'NOCASE' + + *Akshay Vishnoi* + +* Allow the use of symbols or strings to specify enum values in test + fixtures: + + awdr: + title: "Agile Web Development with Rails" + status: :proposed + + *George Claghorn* + +* Clear query cache when `ActiveRecord::Base#reload` is called. + + *Shane Hender, Pierre Nespo* + +* Include stored procedures and function on the MySQL structure dump. + + *Jonathan Worek* + +* Pass `:extend` option for `has_and_belongs_to_many` associations to the + underlying `has_many :through`. + + *Jaehyun Shin* + +* Deprecate `Relation#uniq` use `Relation#distinct` instead. + + See #9683. + + *Yves Senn* + * Allow single table inheritance instantiation to work when storing demodulized class names. @@ -223,7 +328,7 @@ *Josef Šimánek* -* Fixed ActiveRecord::Relation#becomes! and changed_attributes issues for type +* Fixed `ActiveRecord::Relation#becomes!` and `changed_attributes` issues for type columns. Fixes #17139. @@ -406,8 +511,8 @@ *Henrik Nygren* -* Fixed ActiveRecord::Relation#group method when an argument is an SQL - reserved key word: +* Fixed `ActiveRecord::Relation#group` method when an argument is an SQL + reserved keyword: Example: @@ -416,7 +521,7 @@ *Bogdan Gusiev* -* Added the `#or` method on ActiveRecord::Relation, allowing use of the OR +* Added the `#or` method on `ActiveRecord::Relation`, allowing use of the OR operator to combine WHERE or HAVING clauses. Example: @@ -616,7 +721,7 @@ The preferred method to halt a callback chain from now on is to explicitly `throw(:abort)`. - In the past, returning `false` in an ActiveRecord `before_` callback had the + In the past, returning `false` in an Active Record `before_` callback had the side effect of halting the callback chain. This is not recommended anymore and, depending on the value of the `config.active_support.halt_callback_chains_on_return_false` option, will diff --git a/activerecord/Rakefile b/activerecord/Rakefile index fa53926eb2..a619204e6f 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -1,5 +1,4 @@ require 'rake/testtask' -require 'rubygems/package_task' require File.expand_path(File.dirname(__FILE__)) + "/test/config" require File.expand_path(File.dirname(__FILE__)) + "/test/support/config" @@ -151,18 +150,3 @@ task :lines do files = FileList["lib/active_record/**/*.rb"] CodeTools::LineStatistics.new(files).print_loc end - -spec = eval(File.read('activerecord.gemspec')) - -Gem::PackageTask.new(spec) do |p| - p.gem_spec = spec -end - -# Publishing ------------------------------------------------------ - -desc "Release to rubygems" -task :release => :package do - require 'rake/gemcutter' - Rake::Gemcutter::Tasks.new(spec).define - Rake::Task['gem:push'].invoke -end diff --git a/activerecord/bin/test b/activerecord/bin/test new file mode 100755 index 0000000000..f8adf2aabc --- /dev/null +++ b/activerecord/bin/test @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby +COMPONENT_ROOT = File.expand_path("../../", __FILE__) +require File.expand_path("../tools/test", COMPONENT_ROOT) +module Minitest + def self.plugin_active_record_options(opts, options) + opts.separator "" + opts.separator "Active Record options:" + opts.on("-a", "--adapter [ADAPTER]", + "Run tests using a specific adapter (sqlite3, sqlite3_mem, mysql, mysql2, postgresql)") do |adapter| + ENV["ARCONN"] = adapter.strip + end + + opts + end +end + +Minitest.extensions.unshift 'active_record' + +exit Minitest.run(ARGV) diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 1844b29ccb..f5cf92db64 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -54,6 +54,7 @@ module ActiveRecord autoload :QueryCache autoload :Querying autoload :ReadonlyAttributes + autoload :RecordInvalid, 'active_record/validations' autoload :Reflection autoload :RuntimeRegistry autoload :Sanitization diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a3c30642d0..82cb3fed59 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -266,7 +266,6 @@ module ActiveRecord # others.find(*args) | X | X | X # others.exists? | X | X | X # others.distinct | X | X | X - # others.uniq | X | X | X # others.reset | X | X | X # # === Overriding generated methods @@ -956,20 +955,16 @@ module ActiveRecord # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+ # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, - # Active Record doesn't know anything about these inverse relationships and so no object - # loading optimization is possible. For example: + # Active Record can guess the inverse of the association based on the name + # of the class. The result is the following: # # d = Dungeon.first # t = d.traps.first - # d.level == t.dungeon.level # => true - # d.level = 10 - # d.level == t.dungeon.level # => false + # d.object_id == t.dungeon.object_id # => true # # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to - # the same object data from the database, but are actually different in-memory copies - # of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell - # Active Record about inverse relationships and it will optimise object loading. For - # example, if we changed our model definitions to: + # the same in-memory instance since the association matches the name of the class. + # The result would be the same if we added +:inverse_of+ to our model definitions: # # class Dungeon < ActiveRecord::Base # has_many :traps, inverse_of: :dungeon @@ -984,15 +979,14 @@ module ActiveRecord # belongs_to :dungeon, inverse_of: :evil_wizard # end # - # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same - # in-memory instance and our final <tt>d.level == t.dungeon.level</tt> will return +true+. - # # There are limitations to <tt>:inverse_of</tt> support: # # * does not work with <tt>:through</tt> associations. # * does not work with <tt>:polymorphic</tt> associations. # * for +belongs_to+ associations +has_many+ inverse associations are ignored. # + # For more information, see the documentation for the +:inverse_of+ option. + # # == Deleting from associations # # === Dependent associations @@ -1738,7 +1732,7 @@ module ActiveRecord hm_options[:through] = middle_reflection.name hm_options[:source] = join_model.right_reflection.name - [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name].each do |k| + [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k| hm_options[k] = options[k] if options.key? k end diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 265a65c4c1..260a0c6a2d 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -107,7 +107,7 @@ module ActiveRecord end def stale_state - result = owner._read_attribute(reflection.foreign_key) + result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) } result && result.to_s end end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 685c3a5f17..ddeafb40ea 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -470,15 +470,16 @@ module ActiveRecord @association.destroy_all end - # Deletes the +records+ supplied and removes them from the collection. For - # +has_many+ associations, the deletion is done according to the strategy - # specified by the <tt>:dependent</tt> option. Returns an array with the + # Deletes the +records+ supplied from the collection according to the strategy + # specified by the +:dependent+ option. If no +:dependent+ option is given, + # then it will follow the default strategy. Returns an array with the # deleted records. # - # If no <tt>:dependent</tt> option is given, then it will follow the default - # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign - # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default - # strategy is +delete_all+. + # For +has_many :through+ associations, the default deletion strategy is + # +:delete_all+. + # + # For +has_many+ associations, the default deletion strategy is +:nullify+. + # This sets the foreign keys to +NULL+. # # class Person < ActiveRecord::Base # has_many :pets # dependent: :nullify option by default diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb new file mode 100644 index 0000000000..e0bee8c17e --- /dev/null +++ b/activerecord/lib/active_record/attribute/user_provided_default.rb @@ -0,0 +1,32 @@ +require 'active_record/attribute' + +module ActiveRecord + class Attribute # :nodoc: + class UserProvidedDefault < FromUser + def initialize(name, value, type, database_default) + super(name, value, type) + @database_default = database_default + end + + def type_cast(value) + if value.is_a?(Proc) + super(value.call) + else + super + end + end + + def changed_in_place_from?(old_value) + super || changed_from?(database_default.value) + end + + def with_type(type) + self.class.new(name, value_before_type_cast, type, database_default) + end + + protected + + attr_reader :database_default + end + end +end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 7ba907f786..0171ef3bdf 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -108,6 +108,7 @@ module ActiveRecord end def save_changed_attribute(attr, old_value) + clear_changed_attributes_cache if attribute_changed_by_setter?(attr) clear_attribute_changes(attr) unless _field_changed?(attr, old_value) else @@ -176,7 +177,11 @@ module ActiveRecord @cached_changed_attributes = changed_attributes yield ensure - remove_instance_variable(:@cached_changed_attributes) + clear_changed_attributes_cache + end + + def clear_changed_attributes_cache + remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes) end end end diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index d0d8a968c5..e03bf5945d 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -11,6 +11,18 @@ module ActiveRecord # serialized object must be of that class on assignment and retrieval. # Otherwise <tt>SerializationTypeMismatch</tt> will be raised. # + # Empty objects as +{}+, in the case of +Hash+, or +[]+, in the case of + # +Array+, will always be persisted as null. + # + # Keep in mind that database adapters handle certain serialization tasks + # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be + # converted between JSON object/array syntax and Ruby +Hash+ or +Array+ + # objects transparently. There is no need to use +serialize+ in this + # case. + # + # For more complex cases, such as conversion to or from your application + # domain objects, consider using the ActiveRecord::Attributes API. + # # ==== Parameters # # * +attr_name+ - The field name that should be serialized. diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index 50339b6f69..8b2c4c7170 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -1,3 +1,5 @@ +require 'active_record/attribute/user_provided_default' + module ActiveRecord # See ActiveRecord::Attributes::ClassMethods for documentation module Attributes @@ -80,6 +82,14 @@ module ActiveRecord # # StoreListing.new.my_string # => "new default" # + # class Product < ActiveRecord::Base + # attribute :my_default_proc, :datetime, default: -> { Time.now } + # end + # + # Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600 + # sleep 1 + # Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600 + # # Attributes do not need to be backed by a database column. # # class MyModel < ActiveRecord::Base @@ -202,7 +212,8 @@ module ActiveRecord # # +default+ The default value to use when no value is provided. If this option # is not passed, the previous default value (if any) will be used. - # Otherwise, the default will be +nil+. + # Otherwise, the default will be +nil+. A proc can also be passed, and + # will be called once each time a new value is needed. # # +user_provided_default+ Whether the default value should be cast using # +cast+ or +deserialize+. @@ -236,7 +247,12 @@ module ActiveRecord if value == NO_DEFAULT_PROVIDED default_attribute = _default_attributes[name].with_type(type) elsif from_user - default_attribute = Attribute.from_user(name, value, type) + default_attribute = Attribute::UserProvidedDefault.new( + name, + value, + type, + _default_attributes[name], + ) else default_attribute = Attribute.from_database(name, value, type) end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9c5b7d937d..c918e88590 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -5,7 +5,6 @@ require 'active_support/dependencies' require 'active_support/descendants_tracker' require 'active_support/time' require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/slice' 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 ed19819d63..49ffd7ccf0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -587,7 +587,7 @@ module ActiveRecord # # Removes the +index_accounts_on_column+ in the +accounts+ table. # - # remove_index :accounts, :column + # remove_index :accounts, :branch_id # # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table. # @@ -662,7 +662,7 @@ module ActiveRecord # [<tt>:foreign_key</tt>] # Add an appropriate foreign key. Defaults to false. # [<tt>:polymorphic</tt>] - # Wether an additional +_type+ column should be added. Defaults to false. + # Whether an additional +_type+ column should be added. Defaults to false. # # ====== Create a user_id integer column # @@ -777,7 +777,10 @@ module ActiveRecord execute schema_creation.accept(at) end - # Removes the given foreign key from the table. + # Removes the given foreign key from the table. Any option parameters provided + # will be used to re-add the foreign key in case of a migration rollback. + # It is recommended that you provide any options used when creating the foreign + # key so that the migration can be reverted properly. # # Removes the foreign key on +accounts.branch_id+. # @@ -791,6 +794,7 @@ module ActiveRecord # # remove_foreign_key :accounts, name: :special_fk_name # + # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key. def remove_foreign_key(from_table, options_or_to_table = {}) return unless supports_foreign_keys? 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 c3206c6045..00e3d2965b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -959,10 +959,12 @@ module ActiveRecord wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum) variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout) + defaults = [':default', :default].to_set + # Make MySQL reject illegal values rather than truncating or blanking them, see # http://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sqlmode_strict_all_tables # If the user has provided another value for sql_mode, don't replace it. - unless variables.has_key?('sql_mode') + unless variables.has_key?('sql_mode') || defaults.include?(@config[:strict]) variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : '' end @@ -977,7 +979,7 @@ module ActiveRecord # Gather up all of the SET variables... variable_assignments = variables.map do |k, v| - if v == ':default' || v == :default + if defaults.include?(v) "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default elsif !v.nil? "@@SESSION.#{k} = #{quote(v)}" diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index 92349e2f9b..68752cdd80 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -12,6 +12,7 @@ require 'active_record/connection_adapters/postgresql/oid/json' require 'active_record/connection_adapters/postgresql/oid/jsonb' require 'active_record/connection_adapters/postgresql/oid/money' require 'active_record/connection_adapters/postgresql/oid/point' +require 'active_record/connection_adapters/postgresql/oid/rails_5_1_point' require 'active_record/connection_adapters/postgresql/oid/range' require 'active_record/connection_adapters/postgresql/oid/specialized_string' require 'active_record/connection_adapters/postgresql/oid/uuid' diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb index 3de794f797..25961a9869 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb @@ -45,6 +45,11 @@ module ActiveRecord delimiter == other.delimiter end + def type_cast_for_schema(value) + return super unless value.is_a?(::Array) + "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]" + end + private def type_cast_array(value, method) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb index afc9383f91..87391b5dc7 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb @@ -9,7 +9,7 @@ module ActiveRecord def changed_in_place?(raw_old_value, new_value) # Postgres does not preserve insignificant whitespaces when - # roundtripping jsonb columns. This causes some false positives for + # round-tripping jsonb columns. This causes some false positives for # the comparison here. Therefore, we need to parse and re-dump the # raw value here to ensure the insignificant whitespaces are # consistent with our encoder's output. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb new file mode 100644 index 0000000000..7427a25ad5 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb @@ -0,0 +1,50 @@ +module ActiveRecord + Point = Struct.new(:x, :y) + + module ConnectionAdapters + module PostgreSQL + module OID # :nodoc: + class Rails51Point < Type::Value # :nodoc: + include Type::Helpers::Mutable + + def type + :point + end + + def cast(value) + case value + when ::String + if value[0] == '(' && value[-1] == ')' + value = value[1...-1] + end + x, y = value.split(",") + build_point(x, y) + when ::Array + build_point(*value) + else + value + end + end + + def serialize(value) + if value.is_a?(ActiveRecord::Point) + "(#{number_for_point(value.x)},#{number_for_point(value.y)})" + else + super + end + end + + private + + def number_for_point(number) + number.to_s.gsub(/\.0$/, '') + end + + def build_point(x, y) + ActiveRecord::Point.new(Float(x), Float(y)) + end + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 93fa3984e6..2c43c46a3d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -838,6 +838,8 @@ module ActiveRecord ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql) ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql) ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql) + ActiveRecord::Type.register(:legacy_point, OID::Point, adapter: :postgresql) + ActiveRecord::Type.register(:rails_5_1_point, OID::Rails51Point, adapter: :postgresql) ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql) ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql) ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_creation.rb new file mode 100644 index 0000000000..fe1dcbd710 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_creation.rb @@ -0,0 +1,15 @@ +module ActiveRecord + module ConnectionAdapters + module SQLite3 + class SchemaCreation < AbstractAdapter::SchemaCreation + private + def add_column_options!(sql, options) + if options[:collation] + sql << " COLLATE \"#{options[:collation]}\"" + end + super + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 7faca9cb70..87129c42cf 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -1,5 +1,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/statement_pool' +require 'active_record/connection_adapters/sqlite3/schema_creation' gem 'sqlite3', '~> 1.3.6' require 'sqlite3' @@ -84,6 +85,10 @@ module ActiveRecord end end + def schema_creation # :nodoc: + SQLite3::SchemaCreation.new self + end + def initialize(connection, logger, connection_options, config) super(connection, logger) @@ -344,9 +349,10 @@ module ActiveRecord field["dflt_value"] = $1.gsub('""', '"') end + collation = field['collation'] sql_type = field['type'] type_metadata = fetch_type_metadata(sql_type) - new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0) + new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, nil, collation) end end @@ -441,6 +447,7 @@ module ActiveRecord self.null = options[:null] if options.include?(:null) self.precision = options[:precision] if options.include?(:precision) self.scale = options[:scale] if options.include?(:scale) + self.collation = options[:collation] if options.include?(:collation) end end end @@ -454,9 +461,9 @@ module ActiveRecord protected def table_structure(table_name) - structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash + structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA') raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? - structure + table_structure_with_collation(table_name, structure) end def alter_table(table_name, options = {}) #:nodoc: @@ -491,7 +498,7 @@ module ActiveRecord @definition.column(column_name, column.type, :limit => column.limit, :default => column.default, :precision => column.precision, :scale => column.scale, - :null => column.null) + :null => column.null, collation: column.collation) end yield @definition if block_given? end @@ -553,6 +560,46 @@ module ActiveRecord super end end + + private + COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze + + def table_structure_with_collation(table_name, basic_structure) + collation_hash = {} + sql = "SELECT sql FROM + (SELECT * FROM sqlite_master UNION ALL + SELECT * FROM sqlite_temp_master) + WHERE type='table' and name='#{ table_name }' \;" + + # Result will have following sample string + # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + # "password_digest" varchar COLLATE "NOCASE"); + result = exec_query(sql, 'SCHEMA').first + + if result + # Splitting with left parantheses and picking up last will return all + # columns separated with comma(,). + columns_string = result["sql"].split('(').last + + columns_string.split(',').each do |column_string| + # This regex will match the column name and collation type and will save + # the value in $1 and $2 respectively. + collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string) + end + + basic_structure.map! do |column| + column_name = column['name'] + + if collation_hash.has_key? column_name + column['collation'] = collation_hash[column_name] + end + + column + end + else + basic_structure.to_hash + end + end end end end diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 2b99899e42..c0d9d9c1c8 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -75,6 +75,22 @@ module ActiveRecord # # Conversation.where("status <> ?", Conversation.statuses[:archived]) # + # You can use the +:enum_prefix+ or +:enum_suffix+ options when you need + # to define multiple enums with same values. If the passed value is +true+, + # the methods are prefixed/suffixed with the name of the enum. + # + # class Invoice < ActiveRecord::Base + # enum verification: [:done, :fail], enum_prefix: true + # end + # + # It is also possible to supply a custom prefix. + # + # class Invoice < ActiveRecord::Base + # enum verification: [:done, :fail], enum_prefix: :verification_status + # end + # + # Note that <tt>:enum_prefix</tt>/<tt>:enum_suffix</tt> are reserved keywords + # and can not be used as an enum name. module Enum def self.extended(base) # :nodoc: @@ -121,6 +137,8 @@ module ActiveRecord def enum(definitions) klass = self + enum_prefix = definitions.delete(:enum_prefix) + enum_suffix = definitions.delete(:enum_suffix) definitions.each do |name, values| # statuses = { } enum_values = ActiveSupport::HashWithIndifferentAccess.new @@ -138,19 +156,31 @@ module ActiveRecord _enum_methods_module.module_eval do pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index pairs.each do |value, i| + if enum_prefix == true + prefix = "#{name}_" + elsif enum_prefix + prefix = "#{enum_prefix}_" + end + if enum_suffix == true + suffix = "_#{name}" + elsif enum_suffix + suffix = "_#{enum_suffix}" + end + + value_method_name = "#{prefix}#{value}#{suffix}" enum_values[value] = i # def active?() status == 0 end - klass.send(:detect_enum_conflict!, name, "#{value}?") - define_method("#{value}?") { self[name] == value.to_s } + klass.send(:detect_enum_conflict!, name, "#{value_method_name}?") + define_method("#{value_method_name}?") { self[name] == value.to_s } # def active!() update! status: :active end - klass.send(:detect_enum_conflict!, name, "#{value}!") - define_method("#{value}!") { update! name => value } + klass.send(:detect_enum_conflict!, name, "#{value_method_name}!") + define_method("#{value_method_name}!") { update! name => value } # scope :active, -> { where status: 0 } - klass.send(:detect_enum_conflict!, name, value, true) - klass.scope value, -> { klass.where name => value } + klass.send(:detect_enum_conflict!, name, value_method_name, true) + klass.scope value_method_name, -> { klass.where name => value } end end defined_enums[name.to_s] = enum_values diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb index 6a49936644..9adabd7819 100644 --- a/activerecord/lib/active_record/explain_subscriber.rb +++ b/activerecord/lib/active_record/explain_subscriber.rb @@ -19,7 +19,7 @@ module ActiveRecord # On the other hand, we want to monitor the performance of our real database # queries, not the performance of the access to the query cache. IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE) - EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i + EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i def ignore_payload?(payload) payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 2c1771dd6c..b01444a090 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -110,7 +110,7 @@ module ActiveRecord # <% 1.upto(1000) do |i| %> # fix_<%= i %>: # id: <%= i %> - # name: guy_<%= 1 %> + # name: guy_<%= i %> # <% end %> # # This will create 1000 very simple fixtures. @@ -615,7 +615,6 @@ module ActiveRecord # a list of rows to insert to that table. def table_rows now = config.default_timezone == :utc ? Time.now.utc : Time.now - now = now.to_s(:db) # allow a standard key to be used for doing defaults in YAML fixtures.delete('DEFAULTS') @@ -644,6 +643,11 @@ module ActiveRecord row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type) end + # Resolve enums + model_class.defined_enums.each do |name, values| + row[name] = values.fetch(row[name], row[name]) + end + # If STI is used, find the correct subclass for association reflection reflection_class = if row.include?(inheritance_column_name) @@ -664,7 +668,7 @@ module ActiveRecord row[association.foreign_type] = $1 end - fk_type = association.active_record.type_for_attribute(fk_name).type + fk_type = reflection_class.type_for_attribute(fk_name).type row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type) end when :has_many diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b5d7abf41b..d41b7e88d5 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -142,6 +142,9 @@ module ActiveRecord # specified by +column_names+. # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index # specified by +index_name+. + # * <tt>add_reference(:table_name, :reference_name)</tt>: Adds a new column + # +reference_name_id+ by default a integer. See + # ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference for details. # # == Irreversible transformations # diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 36256415df..b592c004aa 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -151,14 +151,16 @@ module ActiveRecord end def invert_remove_index(args) - table, options = *args - - unless options && options.is_a?(Hash) && options[:column] - raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option." + table, options_or_column = *args + if (options = options_or_column).is_a?(Hash) + unless options[:column] + raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option." + end + options = options.dup + [:add_index, [table, options.delete(:column), options]] + elsif (column = options_or_column).present? + [:add_index, [table, column]] end - - options = options.dup - [:add_index, [table, options.delete(:column), options]] end alias :invert_add_belongs_to :invert_add_reference @@ -184,6 +186,16 @@ module ActiveRecord [:remove_foreign_key, [from_table, options]] end + def invert_remove_foreign_key(args) + from_table, to_table, remove_options = args + raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash) + + reversed_args = [from_table, to_table] + reversed_args << remove_options if remove_options + + [:add_foreign_key, reversed_args] + end + # Forwards any missing method call to the \target. def method_missing(method, *args, &block) if @delegate.respond_to?(method) diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 3674f672cb..5a6f42ba09 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -310,6 +310,7 @@ module ActiveRecord def load_schema! @columns_hash = connection.schema_cache.columns_hash(table_name) @columns_hash.each do |name, column| + warn_if_deprecated_type(column) define_attribute( name, connection.lookup_cast_type_from_column(column), @@ -356,6 +357,28 @@ module ActiveRecord base.table_name end end + + def warn_if_deprecated_type(column) + return if attributes_to_define_after_schema_loads.key?(column.name) + if column.respond_to?(:oid) && column.sql_type.start_with?("point") + if column.array? + array_arguments = ", array: true" + else + array_arguments = "" + end + ActiveSupport::Deprecation.warn(<<-WARNING.strip_heredoc) + The behavior of the `:point` type will be changing in Rails 5.1 to + return a `Point` object, rather than an `Array`. If you'd like to + keep the old behavior, you can add this line to #{self.name}: + + attribute :#{column.name}, :legacy_point#{array_arguments} + + If you'd like the new behavior today, you can add this line: + + attribute :#{column.name}, :rails_5_1_point#{array_arguments} + WARNING + end + end end end end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 466175690e..0a6e4ac0bd 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -211,8 +211,7 @@ module ActiveRecord def becomes(klass) became = klass.new became.instance_variable_set("@attributes", @attributes) - changed_attributes = @changed_attributes if defined?(@changed_attributes) - became.instance_variable_set("@changed_attributes", changed_attributes || {}) + became.instance_variable_set("@changed_attributes", attributes_changed_by_setter) became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) became.instance_variable_set("@errors", errors) @@ -382,7 +381,7 @@ module ActiveRecord # # => #<Account id: 1, email: 'account@example.com'> # # Attributes are reloaded from the database, and caches busted, in - # particular the associations cache. + # particular the associations cache and the QueryCache. # # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt> # is raised. Otherwise, in addition to the in-place modification the method @@ -418,6 +417,8 @@ module ActiveRecord # end # def reload(options = nil) + self.class.connection.clear_query_cache + fresh_object = if options && options[:lock] self.class.unscoped { self.class.lock(options[:lock]).find(id) } diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 5af64b717a..da6b8447d3 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -156,8 +156,8 @@ end_warning ActiveSupport.on_load(:active_record) do ActionDispatch::Reloader.send(hook) do if ActiveRecord::Base.connected? - ActiveRecord::Base.clear_reloadable_connections! ActiveRecord::Base.clear_cache! + ActiveRecord::Base.clear_reloadable_connections! end end end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index d168786e71..66fb3ae44b 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -12,7 +12,7 @@ db_namespace = namespace :db do end end - desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV it defaults to creating the development and test databases.' + desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV, it defaults to creating the development and test databases.' task :create => [:load_config] do ActiveRecord::Tasks::DatabaseTasks.create_current end @@ -23,7 +23,7 @@ db_namespace = namespace :db do end end - desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV it defaults to dropping the development and test databases.' + desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV, it defaults to dropping the development and test databases.' task :drop => [:load_config] do ActiveRecord::Tasks::DatabaseTasks.drop_current end @@ -134,10 +134,7 @@ db_namespace = namespace :db do end # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' - task :reset => [:environment, :load_config] do - db_namespace["drop"].invoke - db_namespace["setup"].invoke - end + task :reset => [ 'db:drop', 'db:setup' ] # desc "Retrieves the charset for the current environment's database" task :charset => [:environment, :load_config] do @@ -159,7 +156,7 @@ db_namespace = namespace :db do end # desc "Raises an error if there are pending migrations" - task :abort_if_pending_migrations => :environment do + task :abort_if_pending_migrations => [:environment, :load_config] do pending_migrations = ActiveRecord::Migrator.open(ActiveRecord::Migrator.migrations_paths).pending_migrations if pending_migrations.any? @@ -171,17 +168,17 @@ db_namespace = namespace :db do end end - desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the database first)' + desc 'Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)' task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed] - desc 'Load the seed data from db/seeds.rb' + desc 'Loads the seed data from db/seeds.rb' task :seed do db_namespace['abort_if_pending_migrations'].invoke ActiveRecord::Tasks::DatabaseTasks.load_seed end namespace :fixtures do - desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." + desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task :load => [:environment, :load_config] do require 'active_record/fixtures' @@ -229,7 +226,7 @@ db_namespace = namespace :db do end namespace :schema do - desc 'Create a db/schema.rb file that is portable against any DB supported by AR' + desc 'Creates a db/schema.rb file that is portable against any DB supported by AR' task :dump => [:environment, :load_config] do require 'active_record/schema_dumper' filename = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb') @@ -239,7 +236,7 @@ db_namespace = namespace :db do db_namespace['schema:dump'].reenable end - desc 'Load a schema.rb file into the database' + desc 'Loads a schema.rb file into the database' task :load => [:environment, :load_config] do ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV['SCHEMA']) end @@ -249,7 +246,7 @@ db_namespace = namespace :db do end namespace :cache do - desc 'Create a db/schema_cache.dump file.' + desc 'Creates a db/schema_cache.dump file.' task :dump => [:environment, :load_config] do con = ActiveRecord::Base.connection filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump") @@ -259,7 +256,7 @@ db_namespace = namespace :db do open(filename, 'wb') { |f| f.write(Marshal.dump(con.schema_cache)) } end - desc 'Clear a db/schema_cache.dump file.' + desc 'Clears a db/schema_cache.dump file.' task :clear => [:environment, :load_config] do filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump") FileUtils.rm(filename) if File.exist?(filename) @@ -269,7 +266,7 @@ db_namespace = namespace :db do end namespace :structure do - desc 'Dump the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql' + desc 'Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql' task :dump => [:environment, :load_config] do filename = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql") current_config = ActiveRecord::Tasks::DatabaseTasks.current_config @@ -285,7 +282,7 @@ db_namespace = namespace :db do db_namespace['structure:dump'].reenable end - desc "Recreate the databases from the structure.sql file" + desc "Recreates the databases from the structure.sql file" task :load => [:load_config] do ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['SCHEMA']) end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 85648a7f8f..7d37313058 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -9,7 +9,7 @@ module ActiveRecord :extending, :unscope] SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, - :reverse_order, :distinct, :create_with, :uniq] + :reverse_order, :distinct, :create_with] CLAUSE_METHODS = [:where, :having, :from] INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having] @@ -618,6 +618,7 @@ module ActiveRecord def uniq_value distinct_value end + deprecate uniq_value: :distinct_value # Compares two relations for equality. def ==(other) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 402b317d9c..7a28a98721 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -161,6 +161,10 @@ module ActiveRecord end end + if loaded? && (column_names - @klass.column_names).empty? + return @records.pluck(*column_names) + end + if has_include?(column_names.first) construct_relation_for_association_calculations.pluck(*column_names) else diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index d4a8823cfe..86f2c30168 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -39,7 +39,7 @@ module ActiveRecord BLACKLISTED_ARRAY_METHODS = [ :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!, :shuffle!, :slice!, :sort!, :sort_by!, :delete_if, - :keep_if, :pop, :shift, :delete_at, :compact, :select! + :keep_if, :pop, :shift, :delete_at, :select! ].to_set # :nodoc: delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index c3054f1fe9..dd8f0aa298 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -50,7 +50,7 @@ module ActiveRecord NORMAL_VALUES = Relation::VALUE_METHODS - Relation::CLAUSE_METHODS - - [:joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc: + [:includes, :preload, :joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc: def normal_values NORMAL_VALUES @@ -75,6 +75,7 @@ module ActiveRecord merge_multi_values merge_single_values merge_clauses + merge_preloads merge_joins relation @@ -82,6 +83,27 @@ module ActiveRecord private + def merge_preloads + return if other.preload_values.empty? && other.includes_values.empty? + + if other.klass == relation.klass + relation.preload! other.preload_values unless other.preload_values.empty? + relation.includes! other.includes_values unless other.includes_values.empty? + else + reflection = relation.klass.reflect_on_all_associations.find do |r| + r.class_name == other.klass.name + end || return + + unless other.preload_values.empty? + relation.preload! reflection.name => other.preload_values + end + + unless other.includes_values.empty? + relation.includes! reflection.name => other.includes_values + end + end + end + def merge_joins return if other.joins_values.blank? diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 69ce5cdc2a..f85dc35e89 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -587,7 +587,7 @@ module ActiveRecord # # The two relations must be structurally compatible: they must be scoping the same model, and # they must differ only by +where+ (if no +group+ has been defined) or +having+ (if a +group+ is - # present). Neither relation may have a +limit+, +offset+, or +uniq+ set. + # present). Neither relation may have a +limit+, +offset+, or +distinct+ set. # # Post.where("id = 1").or(Post.where("id = 2")) # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2')) @@ -790,6 +790,7 @@ module ActiveRecord spawn.distinct!(value) end alias uniq distinct + deprecate uniq: :distinct # Like #distinct, but modifies relation in place. def distinct!(value = true) # :nodoc: @@ -797,6 +798,7 @@ module ActiveRecord self end alias uniq! distinct! + deprecate uniq!: :distinct! # Used to extend a scope with additional methods, either through # a module or through a block provided. @@ -999,15 +1001,13 @@ module ActiveRecord end def arel_columns(columns) - if from_clause.value - columns - else - columns.map do |field| - if (Symbol === field || String === field) && columns_hash.key?(field.to_s) - arel_table[field] - else - field - end + columns.map do |field| + if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_clause.value + arel_table[field] + elsif Symbol === field + connection.quote_table_name(field.to_s) + else + field end end end diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index c2567311bd..ba75ffa5a1 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -119,9 +119,9 @@ module ActiveRecord end def replace_named_bind_variables(statement, bind_vars) #:nodoc: - statement.gsub(/(:?):([a-zA-Z]\w*)/) do + statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match| if $1 == ':' # skip postgresql casts - $& # return the whole match + match # return the whole match elsif bind_vars.include?(match = $2.to_sym) replace_bind_variable(bind_vars[match]) else diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb index b5038104ac..cb47bf23f7 100644 --- a/activerecord/lib/active_record/schema_migration.rb +++ b/activerecord/lib/active_record/schema_migration.rb @@ -1,6 +1,5 @@ require 'active_record/scoping/default' require 'active_record/scoping/named' -require 'active_record/base' module ActiveRecord class SchemaMigration < ActiveRecord::Base diff --git a/activerecord/lib/active_record/suppressor.rb b/activerecord/lib/active_record/suppressor.rb index b0b86865fd..b3644bf569 100644 --- a/activerecord/lib/active_record/suppressor.rb +++ b/activerecord/lib/active_record/suppressor.rb @@ -37,8 +37,7 @@ module ActiveRecord end end - # Ignore saving events if we're in suppression mode. - def save!(*args) # :nodoc: + def create_or_update(*args) # :nodoc: SuppressorRegistry.suppressed[self.class.name] ? true : super end end diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index bc80275a88..673386f0d9 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -59,6 +59,7 @@ module ActiveRecord args = prepare_command_options('mysqldump') args.concat(["--result-file", "#{filename}"]) args.concat(["--no-data"]) + args.concat(["--routines"]) args.concat(["#{configuration['database']}"]) unless Kernel.system(*args) $stderr.puts "Could not dump the database structure. "\ diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 311dacb449..6f2def0df1 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -196,9 +196,9 @@ module ActiveRecord # automatically released. The following example demonstrates the problem: # # Model.connection.transaction do # BEGIN - # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1 + # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1 # Model.connection.create_table(...) # active_record_1 now automatically released - # end # RELEASE savepoint active_record_1 + # end # RELEASE SAVEPOINT active_record_1 # # ^^^^ BOOM! database error! # end # diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb index 57eb5d0e18..f0fd95ac16 100644 --- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/connection_helper' -class ActiveSchemaTest < ActiveRecord::TestCase +class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase include ConnectionHelper def setup diff --git a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb index 345122b1ad..98d44315dd 100644 --- a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb +++ b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class MysqlCaseSensitivityTest < ActiveRecord::TestCase +class MysqlCaseSensitivityTest < ActiveRecord::MysqlTestCase class CollationTest < ActiveRecord::Base end diff --git a/activerecord/test/cases/adapters/mysql/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql/charset_collation_test.rb index c8dd49d00a..f2117a97e6 100644 --- a/activerecord/test/cases/adapters/mysql/charset_collation_test.rb +++ b/activerecord/test/cases/adapters/mysql/charset_collation_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class CharsetCollationTest < ActiveRecord::TestCase +class MysqlCharsetCollationTest < ActiveRecord::MysqlTestCase include SchemaDumpingHelper self.use_transactional_tests = false diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index 4762ef43b5..ddbc007b87 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require 'support/connection_helper' require 'support/ddl_helper' -class MysqlConnectionTest < ActiveRecord::TestCase +class MysqlConnectionTest < ActiveRecord::MysqlTestCase include ConnectionHelper include DdlHelper @@ -145,6 +145,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end + def test_mysql_strict_mode_specified_default + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({strict: :default})) + global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode" + session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode" + assert_equal global_sql_mode.rows, session_sql_mode.rows + end + end + def test_mysql_set_session_variable run_without_connection do |orig_connection| ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}})) diff --git a/activerecord/test/cases/adapters/mysql/consistency_test.rb b/activerecord/test/cases/adapters/mysql/consistency_test.rb index ae190b728d..743f6436e4 100644 --- a/activerecord/test/cases/adapters/mysql/consistency_test.rb +++ b/activerecord/test/cases/adapters/mysql/consistency_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class MysqlConsistencyTest < ActiveRecord::TestCase +class MysqlConsistencyTest < ActiveRecord::MysqlTestCase self.use_transactional_tests = false class Consistency < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/mysql/enum_test.rb b/activerecord/test/cases/adapters/mysql/enum_test.rb index f4e7a3ef0a..ef8ee0a6e3 100644 --- a/activerecord/test/cases/adapters/mysql/enum_test.rb +++ b/activerecord/test/cases/adapters/mysql/enum_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class MysqlEnumTest < ActiveRecord::TestCase +class MysqlEnumTest < ActiveRecord::MysqlTestCase class EnumTest < ActiveRecord::Base end diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb index 48ceef365e..b804cb45b9 100644 --- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb +++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb @@ -4,7 +4,7 @@ require 'support/ddl_helper' module ActiveRecord module ConnectionAdapters - class MysqlAdapterTest < ActiveRecord::TestCase + class MysqlAdapterTest < ActiveRecord::MysqlTestCase include DdlHelper def setup diff --git a/activerecord/test/cases/adapters/mysql/quoting_test.rb b/activerecord/test/cases/adapters/mysql/quoting_test.rb index a2206153e9..a296cf9d31 100644 --- a/activerecord/test/cases/adapters/mysql/quoting_test.rb +++ b/activerecord/test/cases/adapters/mysql/quoting_test.rb @@ -1,21 +1,15 @@ require "cases/helper" -module ActiveRecord - module ConnectionAdapters - class MysqlAdapter - class QuotingTest < ActiveRecord::TestCase - def setup - @conn = ActiveRecord::Base.connection - end +class MysqlQuotingTest < ActiveRecord::MysqlTestCase + def setup + @conn = ActiveRecord::Base.connection + end - def test_type_cast_true - assert_equal 1, @conn.type_cast(true) - end + def test_type_cast_true + assert_equal 1, @conn.type_cast(true) + end - def test_type_cast_false - assert_equal 0, @conn.type_cast(false) - end - end - end + def test_type_cast_false + assert_equal 0, @conn.type_cast(false) end end diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb index ec1c394f40..4ea1d9ad36 100644 --- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb @@ -1,29 +1,29 @@ require "cases/helper" -class Group < ActiveRecord::Base - Group.table_name = 'group' - belongs_to :select - has_one :values -end +# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with +# reserved word names (ie: group, order, values, etc...) +class MysqlReservedWordTest < ActiveRecord::MysqlTestCase + class Group < ActiveRecord::Base + Group.table_name = 'group' + belongs_to :select + has_one :values + end -class Select < ActiveRecord::Base - Select.table_name = 'select' - has_many :groups -end + class Select < ActiveRecord::Base + Select.table_name = 'select' + has_many :groups + end -class Values < ActiveRecord::Base - Values.table_name = 'values' -end + class Values < ActiveRecord::Base + Values.table_name = 'values' + end -class Distinct < ActiveRecord::Base - Distinct.table_name = 'distinct' - has_and_belongs_to_many :selects - has_many :values, :through => :groups -end + class Distinct < ActiveRecord::Base + Distinct.table_name = 'distinct' + has_and_belongs_to_many :selects + has_many :values, :through => :groups + end -# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with -# reserved word names (ie: group, order, values, etc...) -class MysqlReservedWordTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb index b7f9c2ce84..2e18f609fd 100644 --- a/activerecord/test/cases/adapters/mysql/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/schema_test.rb @@ -4,7 +4,7 @@ require 'models/comment' module ActiveRecord module ConnectionAdapters - class MysqlSchemaTest < ActiveRecord::TestCase + class MysqlSchemaTest < ActiveRecord::MysqlTestCase fixtures :posts def setup diff --git a/activerecord/test/cases/adapters/mysql/sp_test.rb b/activerecord/test/cases/adapters/mysql/sp_test.rb index 3ca2917ca4..a3d5110032 100644 --- a/activerecord/test/cases/adapters/mysql/sp_test.rb +++ b/activerecord/test/cases/adapters/mysql/sp_test.rb @@ -1,11 +1,11 @@ require "cases/helper" require 'models/topic' -class StoredProcedureTest < ActiveRecord::TestCase +class StoredProcedureTest < ActiveRecord::MysqlTestCase fixtures :topics # Test that MySQL allows multiple results for stored procedures - if Mysql.const_defined?(:CLIENT_MULTI_RESULTS) + if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS) def test_multi_results_from_find_by_sql topics = Topic.find_by_sql 'CALL topics();' assert_equal 1, topics.size diff --git a/activerecord/test/cases/adapters/mysql/sql_types_test.rb b/activerecord/test/cases/adapters/mysql/sql_types_test.rb index 1ddb1b91c9..25b28de7f0 100644 --- a/activerecord/test/cases/adapters/mysql/sql_types_test.rb +++ b/activerecord/test/cases/adapters/mysql/sql_types_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class SqlTypesTest < ActiveRecord::TestCase +class MysqlSqlTypesTest < ActiveRecord::MysqlTestCase def test_binary_types assert_equal 'varbinary(64)', type_to_sql(:binary, 64) assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095) diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb index 209a0cf464..6be36566de 100644 --- a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb @@ -1,23 +1,19 @@ require 'cases/helper' -module ActiveRecord::ConnectionAdapters - class MysqlAdapter - class StatementPoolTest < ActiveRecord::TestCase - if Process.respond_to?(:fork) - def test_cache_is_per_pid - cache = StatementPool.new nil, 10 - cache['foo'] = 'bar' - assert_equal 'bar', cache['foo'] +class MysqlStatementPoolTest < ActiveRecord::MysqlTestCase + if Process.respond_to?(:fork) + def test_cache_is_per_pid + cache = ActiveRecord::ConnectionAdapters::MysqlAdapter::StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] - pid = fork { - lookup = cache['foo']; - exit!(!lookup) - } + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } - Process.waitpid pid - assert $?.success?, 'process should exit successfully' - end - end + Process.waitpid pid + assert $?.success?, 'process should exit successfully' end end end diff --git a/activerecord/test/cases/adapters/mysql/table_options_test.rb b/activerecord/test/cases/adapters/mysql/table_options_test.rb index 0e5b0e8aec..99df6d6cba 100644 --- a/activerecord/test/cases/adapters/mysql/table_options_test.rb +++ b/activerecord/test/cases/adapters/mysql/table_options_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class MysqlTableOptionsTest < ActiveRecord::TestCase +class MysqlTableOptionsTest < ActiveRecord::MysqlTestCase include SchemaDumpingHelper def setup diff --git a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb index e9edc53f93..ed9398a918 100644 --- a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb +++ b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class UnsignedTypeTest < ActiveRecord::TestCase +class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase self.use_transactional_tests = false class UnsignedType < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index 0ea556d4fa..6558d60aa1 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/connection_helper' -class ActiveSchemaTest < ActiveRecord::TestCase +class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase include ConnectionHelper def setup diff --git a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb index 5e8065d80d..abdf3dbf5b 100644 --- a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb +++ b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb @@ -4,7 +4,7 @@ require 'models/topic' module ActiveRecord module ConnectionAdapters class Mysql2Adapter - class BindParameterTest < ActiveRecord::TestCase + class BindParameterTest < ActiveRecord::Mysql2TestCase fixtures :topics def test_update_question_marks diff --git a/activerecord/test/cases/adapters/mysql2/boolean_test.rb b/activerecord/test/cases/adapters/mysql2/boolean_test.rb index 0d81dd6eee..8575df9e43 100644 --- a/activerecord/test/cases/adapters/mysql2/boolean_test.rb +++ b/activerecord/test/cases/adapters/mysql2/boolean_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class Mysql2BooleanTest < ActiveRecord::TestCase +class Mysql2BooleanTest < ActiveRecord::Mysql2TestCase self.use_transactional_tests = false class BooleanType < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb index ccf3d84a44..963116f08a 100644 --- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb +++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class Mysql2CaseSensitivityTest < ActiveRecord::TestCase +class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase class CollationTest < ActiveRecord::Base end diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb index c8dd49d00a..4fd34def15 100644 --- a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb +++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class CharsetCollationTest < ActiveRecord::TestCase +class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase include SchemaDumpingHelper self.use_transactional_tests = false diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index a8b39b21d4..000bcadebe 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/connection_helper' -class MysqlConnectionTest < ActiveRecord::TestCase +class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase include ConnectionHelper fixtures :comments @@ -84,6 +84,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end + def test_mysql_strict_mode_specified_default + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({strict: :default})) + global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode" + session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode" + assert_equal global_sql_mode.rows, session_sql_mode.rows + end + end + def test_mysql_set_session_variable run_without_connection do |orig_connection| ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}})) diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb index 6dd9a5ec87..bd732b5eca 100644 --- a/activerecord/test/cases/adapters/mysql2/enum_test.rb +++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class Mysql2EnumTest < ActiveRecord::TestCase +class Mysql2EnumTest < ActiveRecord::Mysql2TestCase class EnumTest < ActiveRecord::Base end diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb index 2b01d941b8..4fc7414b18 100644 --- a/activerecord/test/cases/adapters/mysql2/explain_test.rb +++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb @@ -5,7 +5,7 @@ require 'models/computer' module ActiveRecord module ConnectionAdapters class Mysql2Adapter - class ExplainTest < ActiveRecord::TestCase + class ExplainTest < ActiveRecord::Mysql2TestCase fixtures :developers def test_explain_for_one_query diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb index 799e60a683..ffb4e2c5cf 100644 --- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -1,29 +1,29 @@ require "cases/helper" -class Group < ActiveRecord::Base - Group.table_name = 'group' - belongs_to :select - has_one :values -end +# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with +# reserved word names (ie: group, order, values, etc...) +class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase + class Group < ActiveRecord::Base + Group.table_name = 'group' + belongs_to :select + has_one :values + end -class Select < ActiveRecord::Base - Select.table_name = 'select' - has_many :groups -end + class Select < ActiveRecord::Base + Select.table_name = 'select' + has_many :groups + end -class Values < ActiveRecord::Base - Values.table_name = 'values' -end + class Values < ActiveRecord::Base + Values.table_name = 'values' + end -class Distinct < ActiveRecord::Base - Distinct.table_name = 'distinct' - has_and_belongs_to_many :selects - has_many :values, :through => :groups -end + class Distinct < ActiveRecord::Base + Distinct.table_name = 'distinct' + has_and_belongs_to_many :selects + has_many :values, :through => :groups + end -# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with -# reserved word names (ie: group, order, values, etc...) -class MysqlReservedWordTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb index 417ccf6d11..396f235e77 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb @@ -1,47 +1,42 @@ require "cases/helper" -module ActiveRecord - module ConnectionAdapters - class AbstractMysqlAdapter - class SchemaMigrationsTest < ActiveRecord::TestCase - def test_renaming_index_on_foreign_key - connection.add_index "engines", "car_id" - connection.add_foreign_key :engines, :cars, name: "fk_engines_cars" - - connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed") - assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name) - ensure - connection.remove_foreign_key :engines, name: "fk_engines_cars" - end - - def test_initializes_schema_migrations_for_encoding_utf8mb4 - smtn = ActiveRecord::Migrator.schema_migrations_table_name - connection.drop_table smtn, if_exists: true - - database_name = connection.current_database - database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'") - - original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"] - original_collation = database_info["DEFAULT_COLLATION_NAME"] - - execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4") - - connection.initialize_schema_migrations_table - - assert connection.column_exists?(smtn, :version, :string, limit: AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN) - ensure - execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}") - end - - private - def connection - @connection ||= ActiveRecord::Base.connection - end - - def execute(sql) - connection.execute(sql) - end - end - end +class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase + def test_renaming_index_on_foreign_key + connection.add_index "engines", "car_id" + connection.add_foreign_key :engines, :cars, name: "fk_engines_cars" + + connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed") + assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name) + ensure + connection.remove_foreign_key :engines, name: "fk_engines_cars" + end + + def test_initializes_schema_migrations_for_encoding_utf8mb4 + smtn = ActiveRecord::Migrator.schema_migrations_table_name + connection.drop_table smtn, if_exists: true + + database_name = connection.current_database + database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'") + + original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"] + original_collation = database_info["DEFAULT_COLLATION_NAME"] + + execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4") + + connection.initialize_schema_migrations_table + + limit = ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN + assert connection.column_exists?(smtn, :version, :string, limit: limit) + ensure + execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}") + end + + private + def connection + @connection ||= ActiveRecord::Base.connection + end + + def execute(sql) + connection.execute(sql) end end diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 47707b7d4f..880a2123d2 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -4,7 +4,7 @@ require 'models/comment' module ActiveRecord module ConnectionAdapters - class Mysql2SchemaTest < ActiveRecord::TestCase + class Mysql2SchemaTest < ActiveRecord::Mysql2TestCase fixtures :posts def setup diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb index 1ddb1b91c9..ae505d29c9 100644 --- a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb +++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class SqlTypesTest < ActiveRecord::TestCase +class Mysql2SqlTypesTest < ActiveRecord::Mysql2TestCase def test_binary_types assert_equal 'varbinary(64)', type_to_sql(:binary, 64) assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095) diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb index 0e5b0e8aec..af121ee7d9 100644 --- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb +++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class MysqlTableOptionsTest < ActiveRecord::TestCase +class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase include SchemaDumpingHelper def setup diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb index e9edc53f93..9e06db2519 100644 --- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb +++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class UnsignedTypeTest < ActiveRecord::TestCase +class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase self.use_transactional_tests = false class UnsignedType < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb index 3808db5141..dc7ba314c6 100644 --- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -1,6 +1,6 @@ require 'cases/helper' -class PostgresqlActiveSchemaTest < ActiveRecord::TestCase +class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase def setup ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do def execute(sql, name = nil) sql end diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index 6edbd9c3a6..380a90d765 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -1,10 +1,9 @@ require "cases/helper" require 'support/schema_dumping_helper' -class PostgresqlArrayTest < ActiveRecord::TestCase +class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper include InTimeZone - OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID class PgArray < ActiveRecord::Base self.table_name = 'pg_arrays' @@ -212,8 +211,9 @@ class PostgresqlArrayTest < ActiveRecord::TestCase def test_quoting_non_standard_delimiters strings = ["hello,", "world;"] - comma_delim = OID::Array.new(ActiveRecord::Type::String.new, ',') - semicolon_delim = OID::Array.new(ActiveRecord::Type::String.new, ';') + oid = ActiveRecord::ConnectionAdapters::PostgreSQL::OID + comma_delim = oid::Array.new(ActiveRecord::Type::String.new, ',') + semicolon_delim = oid::Array.new(ActiveRecord::Type::String.new, ';') assert_equal %({"hello,",world;}), comma_delim.serialize(strings) assert_equal %({hello,;"world;"}), semicolon_delim.serialize(strings) diff --git a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb index 1a5ff4316c..6f72fa6e0f 100644 --- a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require 'support/connection_helper' require 'support/schema_dumping_helper' -class PostgresqlBitStringTest < ActiveRecord::TestCase +class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase include ConnectionHelper include SchemaDumpingHelper diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb index 16db5ab83d..b6bb1929e6 100644 --- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class PostgresqlByteaTest < ActiveRecord::TestCase +class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase class ByteaDataType < ActiveRecord::Base self.table_name = 'bytea_data_type' end diff --git a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb index 5a9796887c..bc12df668d 100644 --- a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb @@ -2,7 +2,7 @@ require 'cases/helper' module ActiveRecord class Migration - class PGChangeSchemaTest < ActiveRecord::TestCase + class PGChangeSchemaTest < ActiveRecord::PostgreSQLTestCase attr_reader :connection def setup diff --git a/activerecord/test/cases/adapters/postgresql/cidr_test.rb b/activerecord/test/cases/adapters/postgresql/cidr_test.rb index 6cb11d17b4..52f2a0096c 100644 --- a/activerecord/test/cases/adapters/postgresql/cidr_test.rb +++ b/activerecord/test/cases/adapters/postgresql/cidr_test.rb @@ -3,8 +3,8 @@ require "ipaddr" module ActiveRecord module ConnectionAdapters - class PostgreSQLAdapter - class CidrTest < ActiveRecord::TestCase + class PostgreSQLAdapter < AbstractAdapter + class CidrTest < ActiveRecord::PostgreSQLTestCase test "type casting IPAddr for database" do type = OID::Cidr.new ip = IPAddr.new("255.0.0.0/8") diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb index f706847890..bd62041e79 100644 --- a/activerecord/test/cases/adapters/postgresql/citext_test.rb +++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb @@ -2,7 +2,7 @@ require 'cases/helper' require 'support/schema_dumping_helper' if ActiveRecord::Base.connection.supports_extensions? - class PostgresqlCitextTest < ActiveRecord::TestCase + class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class Citext < ActiveRecord::Base self.table_name = 'citexts' diff --git a/activerecord/test/cases/adapters/postgresql/collation_test.rb b/activerecord/test/cases/adapters/postgresql/collation_test.rb index 17ef5f304c..8470329c35 100644 --- a/activerecord/test/cases/adapters/postgresql/collation_test.rb +++ b/activerecord/test/cases/adapters/postgresql/collation_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class PostgresqlCollationTest < ActiveRecord::TestCase +class PostgresqlCollationTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper def setup diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb index 16e3f90a47..1de87e5f01 100644 --- a/activerecord/test/cases/adapters/postgresql/composite_test.rb +++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb @@ -40,7 +40,7 @@ end # "unknown OID 5653508: failed to recognize type of 'address'. It will be treated as String." # To take full advantage of composite types, we suggest you register your own +OID::Type+. # See PostgresqlCompositeWithCustomOIDTest -class PostgresqlCompositeTest < ActiveRecord::TestCase +class PostgresqlCompositeTest < ActiveRecord::PostgreSQLTestCase include PostgresqlCompositeBehavior def test_column @@ -77,7 +77,7 @@ class PostgresqlCompositeTest < ActiveRecord::TestCase end end -class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase +class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::PostgreSQLTestCase include PostgresqlCompositeBehavior class FullAddressType < ActiveRecord::Type::Value diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 55ad76c8c0..820d41e13b 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require 'support/connection_helper' module ActiveRecord - class PostgresqlConnectionTest < ActiveRecord::TestCase + class PostgresqlConnectionTest < ActiveRecord::PostgreSQLTestCase include ConnectionHelper class NonExistentTable < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index 2c14252ae4..232c25cb3b 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -11,7 +11,7 @@ end class PostgresqlLtree < ActiveRecord::Base end -class PostgresqlDataTypeTest < ActiveRecord::TestCase +class PostgresqlDataTypeTest < ActiveRecord::PostgreSQLTestCase self.use_transactional_tests = false def setup @@ -69,7 +69,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase end end -class PostgresqlInternalDataTypeTest < ActiveRecord::TestCase +class PostgresqlInternalDataTypeTest < ActiveRecord::PostgreSQLTestCase include DdlHelper setup do diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb index 26e064c937..6102ddacd1 100644 --- a/activerecord/test/cases/adapters/postgresql/domain_test.rb +++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/connection_helper' -class PostgresqlDomainTest < ActiveRecord::TestCase +class PostgresqlDomainTest < ActiveRecord::PostgreSQLTestCase include ConnectionHelper class PostgresqlDomain < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb index ed084483bc..6816a6514b 100644 --- a/activerecord/test/cases/adapters/postgresql/enum_test.rb +++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/connection_helper' -class PostgresqlEnumTest < ActiveRecord::TestCase +class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase include ConnectionHelper class PostgresqlEnum < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb index 6ffb4c9f33..4d0fd640aa 100644 --- a/activerecord/test/cases/adapters/postgresql/explain_test.rb +++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb @@ -2,25 +2,19 @@ require "cases/helper" require 'models/developer' require 'models/computer' -module ActiveRecord - module ConnectionAdapters - class PostgreSQLAdapter - class ExplainTest < ActiveRecord::TestCase - fixtures :developers +class PostgreSQLExplainTest < ActiveRecord::PostgreSQLTestCase + fixtures :developers - def test_explain_for_one_query - explain = Developer.where(:id => 1).explain - assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain - assert_match %(QUERY PLAN), explain - end + def test_explain_for_one_query + explain = Developer.where(:id => 1).explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain + assert_match %(QUERY PLAN), explain + end - def test_explain_with_eager_loading - explain = Developer.where(:id => 1).includes(:audit_logs).explain - assert_match %(QUERY PLAN), explain - assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain - assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain - end - end - end + def test_explain_with_eager_loading + explain = Developer.where(:id => 1).includes(:audit_logs).explain + assert_match %(QUERY PLAN), explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain + assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain end end diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb index 06d427f464..9cfc133308 100644 --- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb +++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class PostgresqlExtensionMigrationTest < ActiveRecord::TestCase +class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase self.use_transactional_tests = false class EnableHstore < ActiveRecord::Migration diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb index b83063c94e..bde7513339 100644 --- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb +++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class PostgresqlFullTextTest < ActiveRecord::TestCase +class PostgresqlFullTextTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class Tsvector < ActiveRecord::Base; end diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb index 41e9572907..0baf985654 100644 --- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb +++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb @@ -2,11 +2,19 @@ require "cases/helper" require 'support/connection_helper' require 'support/schema_dumping_helper' -class PostgresqlPointTest < ActiveRecord::TestCase +class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase include ConnectionHelper include SchemaDumpingHelper - class PostgresqlPoint < ActiveRecord::Base; end + class PostgresqlPoint < ActiveRecord::Base + attribute :x, :rails_5_1_point + attribute :y, :rails_5_1_point + attribute :z, :rails_5_1_point + attribute :array_of_points, :rails_5_1_point, array: true + attribute :legacy_x, :legacy_point + attribute :legacy_y, :legacy_point + attribute :legacy_z, :legacy_point + end def setup @connection = ActiveRecord::Base.connection @@ -14,11 +22,27 @@ class PostgresqlPointTest < ActiveRecord::TestCase t.point :x t.point :y, default: [12.2, 13.3] t.point :z, default: "(14.4,15.5)" + t.point :array_of_points, array: true + t.point :legacy_x + t.point :legacy_y, default: [12.2, 13.3] + t.point :legacy_z, default: "(14.4,15.5)" + end + @connection.create_table('deprecated_points') do |t| + t.point :x end end teardown do @connection.drop_table 'postgresql_points', if_exists: true + @connection.drop_table 'deprecated_points', if_exists: true + end + + class DeprecatedPoint < ActiveRecord::Base; end + + def test_deprecated_legacy_type + assert_deprecated do + DeprecatedPoint.new + end end def test_column @@ -32,11 +56,11 @@ class PostgresqlPointTest < ActiveRecord::TestCase end def test_default - assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['y'] - assert_equal [12.2, 13.3], PostgresqlPoint.new.y + assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.column_defaults['y'] + assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.new.y - assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['z'] - assert_equal [14.4, 15.5], PostgresqlPoint.new.z + assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.column_defaults['z'] + assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.new.z end def test_schema_dumping @@ -49,27 +73,100 @@ class PostgresqlPointTest < ActiveRecord::TestCase def test_roundtrip PostgresqlPoint.create! x: [10, 25.2] record = PostgresqlPoint.first - assert_equal [10, 25.2], record.x + assert_equal ActiveRecord::Point.new(10, 25.2), record.x - record.x = [1.1, 2.2] + record.x = ActiveRecord::Point.new(1.1, 2.2) record.save! assert record.reload - assert_equal [1.1, 2.2], record.x + assert_equal ActiveRecord::Point.new(1.1, 2.2), record.x end def test_mutation - p = PostgresqlPoint.create! x: [10, 20] + p = PostgresqlPoint.create! x: ActiveRecord::Point.new(10, 20) + + p.x.y = 25 + p.save! + p.reload + + assert_equal ActiveRecord::Point.new(10.0, 25.0), p.x + assert_not p.changed? + end + + def test_array_assignment + p = PostgresqlPoint.new(x: [1, 2]) + + assert_equal ActiveRecord::Point.new(1, 2), p.x + end + + def test_string_assignment + p = PostgresqlPoint.new(x: "(1, 2)") + + assert_equal ActiveRecord::Point.new(1, 2), p.x + end + + def test_array_of_points_round_trip + expected_value = [ + ActiveRecord::Point.new(1, 2), + ActiveRecord::Point.new(2, 3), + ActiveRecord::Point.new(3, 4), + ] + p = PostgresqlPoint.new(array_of_points: expected_value) + + assert_equal expected_value, p.array_of_points + p.save! + p.reload + assert_equal expected_value, p.array_of_points + end + + def test_legacy_column + column = PostgresqlPoint.columns_hash["legacy_x"] + assert_equal :point, column.type + assert_equal "point", column.sql_type + assert_not column.array? + + type = PostgresqlPoint.type_for_attribute("legacy_x") + assert_not type.binary? + end + + def test_legacy_default + assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['legacy_y'] + assert_equal [12.2, 13.3], PostgresqlPoint.new.legacy_y + + assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['legacy_z'] + assert_equal [14.4, 15.5], PostgresqlPoint.new.legacy_z + end + + def test_legacy_schema_dumping + output = dump_table_schema("postgresql_points") + assert_match %r{t\.point\s+"legacy_x"$}, output + assert_match %r{t\.point\s+"legacy_y",\s+default: \[12\.2, 13\.3\]$}, output + assert_match %r{t\.point\s+"legacy_z",\s+default: \[14\.4, 15\.5\]$}, output + end + + def test_legacy_roundtrip + PostgresqlPoint.create! legacy_x: [10, 25.2] + record = PostgresqlPoint.first + assert_equal [10, 25.2], record.legacy_x + + record.legacy_x = [1.1, 2.2] + record.save! + assert record.reload + assert_equal [1.1, 2.2], record.legacy_x + end + + def test_legacy_mutation + p = PostgresqlPoint.create! legacy_x: [10, 20] - p.x[1] = 25 + p.legacy_x[1] = 25 p.save! p.reload - assert_equal [10.0, 25.0], p.x + assert_equal [10.0, 25.0], p.legacy_x assert_not p.changed? end end -class PostgresqlGeometricTest < ActiveRecord::TestCase +class PostgresqlGeometricTest < ActiveRecord::PostgreSQLTestCase class PostgresqlGeometric < ActiveRecord::Base; end setup do diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index ad9dd311a6..6a2d501646 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require 'support/schema_dumping_helper' if ActiveRecord::Base.connection.supports_extensions? - class PostgresqlHstoreTest < ActiveRecord::TestCase + class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class Hstore < ActiveRecord::Base self.table_name = 'hstores' diff --git a/activerecord/test/cases/adapters/postgresql/infinity_test.rb b/activerecord/test/cases/adapters/postgresql/infinity_test.rb index d9d7832094..bfda933fa4 100644 --- a/activerecord/test/cases/adapters/postgresql/infinity_test.rb +++ b/activerecord/test/cases/adapters/postgresql/infinity_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class PostgresqlInfinityTest < ActiveRecord::TestCase +class PostgresqlInfinityTest < ActiveRecord::PostgreSQLTestCase include InTimeZone class PostgresqlInfinity < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/postgresql/integer_test.rb b/activerecord/test/cases/adapters/postgresql/integer_test.rb index 679a0fc7b3..b4e55964b9 100644 --- a/activerecord/test/cases/adapters/postgresql/integer_test.rb +++ b/activerecord/test/cases/adapters/postgresql/integer_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require "active_support/core_ext/numeric/bytes" -class PostgresqlIntegerTest < ActiveRecord::TestCase +class PostgresqlIntegerTest < ActiveRecord::PostgreSQLTestCase class PgInteger < ActiveRecord::Base end diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index 6878516aeb..f242f32496 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -188,7 +188,7 @@ module PostgresqlJSONSharedTestCases end end -class PostgresqlJSONTest < ActiveRecord::TestCase +class PostgresqlJSONTest < ActiveRecord::PostgreSQLTestCase include PostgresqlJSONSharedTestCases def column_type @@ -196,7 +196,7 @@ class PostgresqlJSONTest < ActiveRecord::TestCase end end -class PostgresqlJSONBTest < ActiveRecord::TestCase +class PostgresqlJSONBTest < ActiveRecord::PostgreSQLTestCase include PostgresqlJSONSharedTestCases def column_type diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb index ce0ad16557..56516c82b4 100644 --- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb +++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class PostgresqlLtreeTest < ActiveRecord::TestCase +class PostgresqlLtreeTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class Ltree < ActiveRecord::Base self.table_name = 'ltrees' diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb index cedd399380..e3670c203d 100644 --- a/activerecord/test/cases/adapters/postgresql/money_test.rb +++ b/activerecord/test/cases/adapters/postgresql/money_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class PostgresqlMoneyTest < ActiveRecord::TestCase +class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class PostgresqlMoney < ActiveRecord::Base; end diff --git a/activerecord/test/cases/adapters/postgresql/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb index 033695518e..fe6ee4e2d9 100644 --- a/activerecord/test/cases/adapters/postgresql/network_test.rb +++ b/activerecord/test/cases/adapters/postgresql/network_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class PostgresqlNetworkTest < ActiveRecord::TestCase +class PostgresqlNetworkTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class PostgresqlNetworkAddress < ActiveRecord::Base; end diff --git a/activerecord/test/cases/adapters/postgresql/numbers_test.rb b/activerecord/test/cases/adapters/postgresql/numbers_test.rb index d8e01e3b89..ba7e7dc9a3 100644 --- a/activerecord/test/cases/adapters/postgresql/numbers_test.rb +++ b/activerecord/test/cases/adapters/postgresql/numbers_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class PostgresqlNumberTest < ActiveRecord::TestCase +class PostgresqlNumberTest < ActiveRecord::PostgreSQLTestCase class PostgresqlNumber < ActiveRecord::Base; end setup do diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index 9a1b889d4d..6e6850c4a9 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -4,7 +4,7 @@ require 'support/connection_helper' module ActiveRecord module ConnectionAdapters - class PostgreSQLAdapterTest < ActiveRecord::TestCase + class PostgreSQLAdapterTest < ActiveRecord::PostgreSQLTestCase include DdlHelper include ConnectionHelper diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb index e4420d9d13..5e6f4dbbb8 100644 --- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb +++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb @@ -4,7 +4,7 @@ require 'ipaddr' module ActiveRecord module ConnectionAdapters class PostgreSQLAdapter - class QuotingTest < ActiveRecord::TestCase + class QuotingTest < ActiveRecord::PostgreSQLTestCase def setup @conn = ActiveRecord::Base.connection end diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index bbf96278b0..02b1083430 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -1,12 +1,12 @@ require "cases/helper" require 'support/connection_helper' -if ActiveRecord::Base.connection.supports_ranges? +if ActiveRecord::Base.connection.respond_to?(:supports_ranges?) && ActiveRecord::Base.connection.supports_ranges? class PostgresqlRange < ActiveRecord::Base self.table_name = "postgresql_ranges" end - class PostgresqlRangeTest < ActiveRecord::TestCase + class PostgresqlRangeTest < ActiveRecord::PostgreSQLTestCase self.use_transactional_tests = false include ConnectionHelper diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb index 7200ed2771..c895ab9db5 100644 --- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb +++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb @@ -1,7 +1,7 @@ require 'cases/helper' require 'support/connection_helper' -class PostgreSQLReferentialIntegrityTest < ActiveRecord::TestCase +class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase self.use_transactional_tests = false include ConnectionHelper diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb index f507328868..bd64bae308 100644 --- a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb +++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class PostgresqlRenameTableTest < ActiveRecord::TestCase +class PostgresqlRenameTableTest < ActiveRecord::PostgreSQLTestCase def setup @connection = ActiveRecord::Base.connection @connection.create_table :before_rename, force: true diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb index 359a45bbd1..fa6584eae5 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb @@ -3,7 +3,7 @@ require "cases/helper" class SchemaThing < ActiveRecord::Base end -class SchemaAuthorizationTest < ActiveRecord::TestCase +class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase self.use_transactional_tests = false TABLE_NAME = 'schema_things' diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index f925dcad97..9aba2b5976 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require 'models/default' require 'support/schema_dumping_helper' -class SchemaTest < ActiveRecord::TestCase +class SchemaTest < ActiveRecord::PostgreSQLTestCase self.use_transactional_tests = false SCHEMA_NAME = 'test_schema' @@ -441,7 +441,7 @@ class SchemaTest < ActiveRecord::TestCase end end -class SchemaForeignKeyTest < ActiveRecord::TestCase +class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper setup do @@ -466,7 +466,7 @@ class SchemaForeignKeyTest < ActiveRecord::TestCase end end -class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase +class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase setup do @connection = ActiveRecord::Base.connection @connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE" diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb index 458a8dae6c..7d30db247b 100644 --- a/activerecord/test/cases/adapters/postgresql/serial_test.rb +++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/schema_dumping_helper' -class PostgresqlSerialTest < ActiveRecord::TestCase +class PostgresqlSerialTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class PostgresqlSerial < ActiveRecord::Base; end @@ -30,7 +30,7 @@ class PostgresqlSerialTest < ActiveRecord::TestCase end end -class PostgresqlBigSerialTest < ActiveRecord::TestCase +class PostgresqlBigSerialTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class PostgresqlBigSerial < ActiveRecord::Base; end diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb index 1497b0abc7..5aab246c99 100644 --- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb @@ -13,7 +13,7 @@ module ActiveRecord end end - class StatementPoolTest < ActiveRecord::TestCase + class StatementPoolTest < ActiveRecord::PostgreSQLTestCase if Process.respond_to?(:fork) def test_cache_is_per_pid cache = StatementPool.new nil, 10 diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb index a639f98272..4c4866b46b 100644 --- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb +++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb @@ -2,7 +2,7 @@ require 'cases/helper' require 'models/developer' require 'models/topic' -class PostgresqlTimestampTest < ActiveRecord::TestCase +class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase class PostgresqlTimestampWithZone < ActiveRecord::Base; end self.use_transactional_tests = false @@ -43,7 +43,7 @@ class PostgresqlTimestampTest < ActiveRecord::TestCase end end -class TimestampTest < ActiveRecord::TestCase +class PostgresqlTimestampFixtureTest < ActiveRecord::PostgreSQLTestCase fixtures :topics def test_group_by_date diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb index c0907b8f21..77a99ca778 100644 --- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb +++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb @@ -1,6 +1,6 @@ require 'cases/helper' -class PostgresqlTypeLookupTest < ActiveRecord::TestCase +class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase setup do @connection = ActiveRecord::Base.connection end diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb index 3fdb6888d9..095c1826e5 100644 --- a/activerecord/test/cases/adapters/postgresql/utils_test.rb +++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb @@ -1,6 +1,7 @@ require 'cases/helper' +require 'active_record/connection_adapters/postgresql/utils' -class PostgreSQLUtilsTest < ActiveSupport::TestCase +class PostgreSQLUtilsTest < ActiveRecord::PostgreSQLTestCase Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name include ActiveRecord::ConnectionAdapters::PostgreSQL::Utils @@ -20,7 +21,7 @@ class PostgreSQLUtilsTest < ActiveSupport::TestCase end end -class PostgreSQLNameTest < ActiveSupport::TestCase +class PostgreSQLNameTest < ActiveRecord::PostgreSQLTestCase Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name test "represents itself as schema.name" do diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index e9379a1019..7127d69e9e 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -11,7 +11,7 @@ module PostgresqlUUIDHelper end end -class PostgresqlUUIDTest < ActiveRecord::TestCase +class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase include PostgresqlUUIDHelper include SchemaDumpingHelper @@ -135,7 +135,7 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase end end -class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase +class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase include PostgresqlUUIDHelper include SchemaDumpingHelper @@ -210,7 +210,7 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase end end -class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase +class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase include PostgresqlUUIDHelper include SchemaDumpingHelper @@ -244,7 +244,7 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase end end -class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase +class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase include PostgresqlUUIDHelper class UuidPost < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/postgresql/view_test.rb b/activerecord/test/cases/adapters/postgresql/view_test.rb index 8a8e1d3b17..2dd6ec5fe6 100644 --- a/activerecord/test/cases/adapters/postgresql/view_test.rb +++ b/activerecord/test/cases/adapters/postgresql/view_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require "cases/view_test" -class UpdateableViewTest < ActiveRecord::TestCase +class UpdateableViewTest < ActiveRecord::PostgreSQLTestCase fixtures :books class PrintedBook < ActiveRecord::Base @@ -46,8 +46,9 @@ class UpdateableViewTest < ActiveRecord::TestCase end end -if ActiveRecord::Base.connection.supports_materialized_views? -class MaterializedViewTest < ActiveRecord::TestCase +if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) && + ActiveRecord::Base.connection.supports_materialized_views? +class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase include ViewBehavior private diff --git a/activerecord/test/cases/adapters/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb index b097deb2f4..add32699fa 100644 --- a/activerecord/test/cases/adapters/postgresql/xml_test.rb +++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb @@ -1,7 +1,7 @@ require 'cases/helper' require 'support/schema_dumping_helper' -class PostgresqlXMLTest < ActiveRecord::TestCase +class PostgresqlXMLTest < ActiveRecord::PostgreSQLTestCase include SchemaDumpingHelper class XmlDataType < ActiveRecord::Base self.table_name = 'xml_data_type' diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb new file mode 100644 index 0000000000..58a9469ce5 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb @@ -0,0 +1,53 @@ +require "cases/helper" +require 'support/schema_dumping_helper' + +class SQLite3CollationTest < ActiveRecord::SQLite3TestCase + include SchemaDumpingHelper + + def setup + @connection = ActiveRecord::Base.connection + @connection.create_table :collation_table_sqlite3, force: true do |t| + t.string :string_nocase, collation: 'NOCASE' + t.text :text_rtrim, collation: 'RTRIM' + end + end + + def teardown + @connection.drop_table :collation_table_sqlite3, if_exists: true + end + + test "string column with collation" do + column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'string_nocase' } + assert_equal :string, column.type + assert_equal 'NOCASE', column.collation + end + + test "text column with collation" do + column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'text_rtrim' } + assert_equal :text, column.type + assert_equal 'RTRIM', column.collation + end + + test "add column with collation" do + @connection.add_column :collation_table_sqlite3, :title, :string, collation: 'RTRIM' + + column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'title' } + assert_equal :string, column.type + assert_equal 'RTRIM', column.collation + end + + test "change column with collation" do + @connection.add_column :collation_table_sqlite3, :description, :string + @connection.change_column :collation_table_sqlite3, :description, :text, collation: 'RTRIM' + + column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'description' } + assert_equal :text, column.type + assert_equal 'RTRIM', column.collation + end + + test "schema dump includes collation" do + output = dump_table_schema("collation_table_sqlite3") + assert_match %r{t.string\s+"string_nocase",\s+collation: "NOCASE"$}, output + assert_match %r{t.text\s+"text_rtrim",\s+collation: "RTRIM"$}, output + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb index 13b754d226..34e3b2e023 100644 --- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class CopyTableTest < ActiveRecord::TestCase +class CopyTableTest < ActiveRecord::SQLite3TestCase fixtures :customers def setup diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb index 7d66c44798..2aec322582 100644 --- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb @@ -5,7 +5,7 @@ require 'models/computer' module ActiveRecord module ConnectionAdapters class SQLite3Adapter - class ExplainTest < ActiveRecord::TestCase + class ExplainTest < ActiveRecord::SQLite3TestCase fixtures :developers def test_explain_for_one_query diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb index 243f65df98..87a892db37 100644 --- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb @@ -6,7 +6,7 @@ require 'securerandom' module ActiveRecord module ConnectionAdapters class SQLite3Adapter - class QuotingTest < ActiveRecord::TestCase + class QuotingTest < ActiveRecord::SQLite3TestCase def setup @conn = Base.sqlite3_connection :database => ':memory:', :adapter => 'sqlite3', diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 27f4ba8eb6..7996e7ad50 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -5,7 +5,7 @@ require 'support/ddl_helper' module ActiveRecord module ConnectionAdapters - class SQLite3AdapterTest < ActiveRecord::TestCase + class SQLite3AdapterTest < ActiveRecord::SQLite3TestCase include DdlHelper self.use_transactional_tests = false @@ -421,14 +421,14 @@ module ActiveRecord end def test_statement_closed - db = SQLite3::Database.new(ActiveRecord::Base. + db = ::SQLite3::Database.new(ActiveRecord::Base. configurations['arunit']['database']) - statement = SQLite3::Statement.new(db, + statement = ::SQLite3::Statement.new(db, 'CREATE TABLE statement_test (number integer not null)') - statement.stubs(:step).raises(SQLite3::BusyException, 'busy') + statement.stubs(:step).raises(::SQLite3::BusyException, 'busy') statement.stubs(:columns).once.returns([]) statement.expects(:close).once - SQLite3::Statement.stubs(:new).returns(statement) + ::SQLite3::Statement.stubs(:new).returns(statement) assert_raises ActiveRecord::StatementInvalid do @conn.exec_query 'select * from statement_test' diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb index deedf67c8e..887dcfc96c 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb @@ -3,7 +3,7 @@ require 'models/owner' module ActiveRecord module ConnectionAdapters - class SQLite3CreateFolder < ActiveRecord::TestCase + class SQLite3CreateFolder < ActiveRecord::SQLite3TestCase def test_sqlite_creates_directory Dir.mktmpdir do |dir| dir = Pathname.new(dir) diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb index fd0044ac05..ef324183a7 100644 --- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb @@ -2,7 +2,7 @@ require 'cases/helper' module ActiveRecord::ConnectionAdapters class SQLite3Adapter - class StatementPoolTest < ActiveRecord::TestCase + class StatementPoolTest < ActiveRecord::SQLite3TestCase if Process.respond_to?(:fork) def test_cache_is_per_pid @@ -22,4 +22,3 @@ module ActiveRecord::ConnectionAdapters end end end - diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index ba90c61d65..039cc46b0b 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -31,6 +31,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal companies(:first_firm).name, firm.name end + def test_missing_attribute_error_is_raised_when_no_foreign_key_attribute + assert_raises(ActiveModel::MissingAttributeError) { Client.select(:id).first.firm } + end + def test_belongs_to_does_not_use_order_by ActiveRecord::SQLCounter.clear_log Client.find(3).firm diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 0ecf2ddfd1..ffbf60e390 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1167,7 +1167,7 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_no_queries { assert client.accounts.empty? } end - def test_preloading_has_many_through_with_uniq + def test_preloading_has_many_through_with_distinct mary = Author.includes(:unique_categorized_posts).where(:id => authors(:mary).id).first assert_equal 1, mary.unique_categorized_posts.length assert_equal 1, mary.unique_categorized_post_ids.length diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index aea9207bfe..e584c94ad8 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -83,6 +83,16 @@ class DeveloperWithSymbolClassName < Developer has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys end +class DeveloperWithExtendOption < Developer + module NamedExtension + def category + 'sns' + end + end + + has_and_belongs_to_many :projects, extend: NamedExtension +end + class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers @@ -234,7 +244,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal developers, new_project.developers end - def test_habtm_unique_order_preserved + def test_habtm_distinct_order_preserved assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers end @@ -339,7 +349,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 'Yet Another Testing Title', another_post.title end - def test_uniq_after_the_fact + def test_distinct_after_the_fact dev = developers(:jamis) dev.projects << projects(:active_record) dev.projects << projects(:active_record) @@ -348,13 +358,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 1, dev.projects.distinct.size end - def test_uniq_before_the_fact + def test_distinct_before_the_fact projects(:active_record).developers << developers(:jamis) projects(:active_record).developers << developers(:david) assert_equal 3, projects(:active_record, :reload).developers.size end - def test_uniq_option_prevents_duplicate_push + def test_distinct_option_prevents_duplicate_push project = projects(:active_record) project.developers << developers(:jamis) project.developers << developers(:david) @@ -365,7 +375,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 3, project.developers.size end - def test_uniq_when_association_already_loaded + def test_distinct_when_association_already_loaded project = projects(:active_record) project.developers << [ developers(:jamis), developers(:david), developers(:jamis), developers(:david) ] assert_equal 3, Project.includes(:developers).find(project.id).developers.size @@ -577,6 +587,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal developers(:poor_jamis), projects(:active_record).developers.where("salary < 10000").first end + def test_association_with_extend_option + eponine = DeveloperWithExtendOption.create(name: 'Eponine') + assert_equal 'sns', eponine.projects.category + end + def test_replace_with_less david = developers(:david) david.projects = [projects(:action_controller)] diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 190cef55c4..1d545af5a5 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -960,7 +960,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal 1, category.categorizations.where(:special => true).count end - def test_joining_has_many_through_with_uniq + def test_joining_has_many_through_with_distinct mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first assert_equal 1, mary.unique_categorized_posts.length assert_equal 1, mary.unique_categorized_post_ids.length diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 213be50e67..5575419c35 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -35,12 +35,12 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert categories(:sti_test).authors.include?(authors(:mary)) end - def test_has_many_uniq_through_join_model + def test_has_many_distinct_through_join_model assert_equal 2, authors(:mary).categorized_posts.size assert_equal 1, authors(:mary).unique_categorized_posts.size end - def test_has_many_uniq_through_count + def test_has_many_distinct_through_count author = authors(:mary) assert !authors(:mary).unique_categorized_posts.loaded? assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count } @@ -49,7 +49,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert !authors(:mary).unique_categorized_posts.loaded? end - def test_has_many_uniq_through_find + def test_has_many_distinct_through_find assert_equal 1, authors(:mary).unique_categorized_posts.to_a.size end @@ -625,7 +625,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments end - def test_uniq_has_many_through_should_retain_order + def test_distinct_has_many_through_should_retain_order comment_ids = authors(:david).comments.map(&:id) assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id) assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id) diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb index 927d7950a5..eeda9335ad 100644 --- a/activerecord/test/cases/attributes_test.rb +++ b/activerecord/test/cases/attributes_test.rb @@ -125,6 +125,22 @@ module ActiveRecord assert_equal "from user", model.wibble end + test "procs for default values" do + klass = Class.new(OverloadedType) do + @@counter = 0 + attribute :counter, :integer, default: -> { @@counter += 1 } + end + + assert_equal 1, klass.new.counter + assert_equal 2, klass.new.counter + end + + test "user provided defaults are persisted even if unchanged" do + model = OverloadedType.create! + + assert_equal "the overloaded default", model.reload.string_with_default + end + if current_adapter?(:PostgreSQLAdapter) test "arrays types can be specified" do klass = Class.new(OverloadedType) do diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 4306738670..31c31e4329 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1411,15 +1411,13 @@ class BasicsTest < ActiveRecord::TestCase end def test_uniq_delegates_to_scoped - scope = stub - Bird.stubs(:all).returns(mock(:uniq => scope)) - assert_equal scope, Bird.uniq + assert_deprecated do + assert_equal Bird.all.distinct, Bird.uniq + end end def test_distinct_delegates_to_scoped - scope = stub - Bird.stubs(:all).returns(mock(:distinct => scope)) - assert_equal scope, Bird.distinct + assert_equal Bird.all.distinct, Bird.distinct end def test_table_name_with_2_abstract_subclasses diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 8fc996ee74..cb4681109c 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -359,7 +359,10 @@ class CalculationsTest < ActiveRecord::TestCase def test_count_with_distinct assert_equal 4, Account.select(:credit_limit).distinct.count - assert_equal 4, Account.select(:credit_limit).uniq.count + + assert_deprecated do + assert_equal 4, Account.select(:credit_limit).uniq.count + end end def test_count_with_aliased_attribute @@ -504,8 +507,8 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal [ topic.written_on ], relation.pluck(:written_on) end - def test_pluck_and_uniq - assert_equal [50, 53, 55, 60], Account.order(:credit_limit).uniq.pluck(:credit_limit) + def test_pluck_and_distinct + assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit) end def test_pluck_in_relation @@ -629,6 +632,27 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal [part.id], ShipPart.joins(:trinkets).pluck(:id) end + def test_pluck_loaded_relation + companies = Company.order(:id).limit(3).load + assert_no_queries do + assert_equal ['37signals', 'Summit', 'Microsoft'], companies.pluck(:name) + end + end + + def test_pluck_loaded_relation_multiple_columns + companies = Company.order(:id).limit(3).load + assert_no_queries do + assert_equal [[1, '37signals'], [2, 'Summit'], [3, 'Microsoft']], companies.pluck(:id, :name) + end + end + + def test_pluck_loaded_relation_sql_fragment + companies = Company.order(:name).limit(3).load + assert_queries 1 do + assert_equal ['37signals', 'Apex', 'Ex Nihilo'], companies.pluck('DISTINCT name') + end + end + def test_grouped_calculation_with_polymorphic_relation part = ShipPart.create!(name: "has trinket") part.trinkets.create! diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb index 05c57985a1..7566863653 100644 --- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb +++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb @@ -81,7 +81,11 @@ module ActiveRecord def test_bigint_limit cast_type = @connection.type_map.lookup("bigint") - assert_equal 8, cast_type.limit + if current_adapter?(:OracleAdapter) + assert_equal 19, cast_type.limit + else + assert_equal 8, cast_type.limit + end end def test_decimal_without_scale diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 3a7cc572e6..f5aaf22e13 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -623,32 +623,6 @@ class DirtyTest < ActiveRecord::TestCase end end - test "defaults with type that implements `serialize`" do - type = Class.new(ActiveRecord::Type::Value) do - def cast(value) - value.to_i - end - - def serialize(value) - value.to_s - end - end - - model_class = Class.new(ActiveRecord::Base) do - self.table_name = 'numeric_data' - attribute :foo, type.new, default: 1 - end - - model = model_class.new - assert_not model.foo_changed? - - model = model_class.new(foo: 1) - assert_not model.foo_changed? - - model = model_class.new(foo: '1') - assert_not model.foo_changed? - end - test "in place mutation detection" do pirate = Pirate.create!(catchphrase: "arrrr") pirate.catchphrase << " matey!" @@ -729,6 +703,22 @@ class DirtyTest < ActiveRecord::TestCase assert pirate.catchphrase_changed?(from: "arrrr", to: "arrrr matey!") end + test "getters with side effects are allowed" do + klass = Class.new(Pirate) do + def catchphrase + if super.blank? + update_attribute(:catchphrase, "arr") # what could possibly go wrong? + end + super + end + end + + pirate = klass.create!(catchphrase: "lol") + pirate.update_attribute(:catchphrase, nil) + + assert_equal "arr", pirate.catchphrase + end + private def with_partial_writes(klass, on = true) old = klass.partial_writes? diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb index eea184e530..769b171717 100644 --- a/activerecord/test/cases/enum_test.rb +++ b/activerecord/test/cases/enum_test.rb @@ -9,49 +9,59 @@ class EnumTest < ActiveRecord::TestCase end test "query state by predicate" do - assert @book.proposed? + assert @book.published? assert_not @book.written? - assert_not @book.published? + assert_not @book.proposed? - assert @book.unread? + assert @book.read? + assert @book.in_english? + assert @book.author_visibility_visible? + assert @book.illustrator_visibility_visible? + assert @book.with_medium_font_size? end test "query state with strings" do - assert_equal "proposed", @book.status - assert_equal "unread", @book.read_status + assert_equal "published", @book.status + assert_equal "read", @book.read_status + assert_equal "english", @book.language + assert_equal "visible", @book.author_visibility + assert_equal "visible", @book.illustrator_visibility end test "find via scope" do - assert_equal @book, Book.proposed.first - assert_equal @book, Book.unread.first + assert_equal @book, Book.published.first + assert_equal @book, Book.read.first + assert_equal @book, Book.in_english.first + assert_equal @book, Book.author_visibility_visible.first + assert_equal @book, Book.illustrator_visibility_visible.first end test "find via where with values" do - proposed, written = Book.statuses[:proposed], Book.statuses[:written] + published, written = Book.statuses[:published], Book.statuses[:written] - assert_equal @book, Book.where(status: proposed).first + assert_equal @book, Book.where(status: published).first refute_equal @book, Book.where(status: written).first - assert_equal @book, Book.where(status: [proposed]).first + assert_equal @book, Book.where(status: [published]).first refute_equal @book, Book.where(status: [written]).first - refute_equal @book, Book.where("status <> ?", proposed).first + refute_equal @book, Book.where("status <> ?", published).first assert_equal @book, Book.where("status <> ?", written).first end test "find via where with symbols" do - assert_equal @book, Book.where(status: :proposed).first + assert_equal @book, Book.where(status: :published).first refute_equal @book, Book.where(status: :written).first - assert_equal @book, Book.where(status: [:proposed]).first + assert_equal @book, Book.where(status: [:published]).first refute_equal @book, Book.where(status: [:written]).first - refute_equal @book, Book.where.not(status: :proposed).first + refute_equal @book, Book.where.not(status: :published).first assert_equal @book, Book.where.not(status: :written).first end test "find via where with strings" do - assert_equal @book, Book.where(status: "proposed").first + assert_equal @book, Book.where(status: "published").first refute_equal @book, Book.where(status: "written").first - assert_equal @book, Book.where(status: ["proposed"]).first + assert_equal @book, Book.where(status: ["published"]).first refute_equal @book, Book.where(status: ["written"]).first - refute_equal @book, Book.where.not(status: "proposed").first + refute_equal @book, Book.where.not(status: "published").first assert_equal @book, Book.where.not(status: "written").first end @@ -72,6 +82,10 @@ class EnumTest < ActiveRecord::TestCase test "update by declaration" do @book.written! assert @book.written? + @book.in_english! + assert @book.in_english? + @book.author_visibility_visible! + assert @book.author_visibility_visible? end test "update by setter" do @@ -96,42 +110,61 @@ class EnumTest < ActiveRecord::TestCase test "enum changed attributes" do old_status = @book.status - @book.status = :published + old_language = @book.language + @book.status = :proposed + @book.language = :spanish assert_equal old_status, @book.changed_attributes[:status] + assert_equal old_language, @book.changed_attributes[:language] end test "enum changes" do old_status = @book.status - @book.status = :published - assert_equal [old_status, 'published'], @book.changes[:status] + old_language = @book.language + @book.status = :proposed + @book.language = :spanish + assert_equal [old_status, 'proposed'], @book.changes[:status] + assert_equal [old_language, 'spanish'], @book.changes[:language] end test "enum attribute was" do old_status = @book.status + old_language = @book.language @book.status = :published + @book.language = :spanish assert_equal old_status, @book.attribute_was(:status) + assert_equal old_language, @book.attribute_was(:language) end test "enum attribute changed" do - @book.status = :published + @book.status = :proposed + @book.language = :french assert @book.attribute_changed?(:status) + assert @book.attribute_changed?(:language) end test "enum attribute changed to" do - @book.status = :published - assert @book.attribute_changed?(:status, to: 'published') + @book.status = :proposed + @book.language = :french + assert @book.attribute_changed?(:status, to: 'proposed') + assert @book.attribute_changed?(:language, to: 'french') end test "enum attribute changed from" do old_status = @book.status - @book.status = :published + old_language = @book.language + @book.status = :proposed + @book.language = :french assert @book.attribute_changed?(:status, from: old_status) + assert @book.attribute_changed?(:language, from: old_language) end test "enum attribute changed from old status to new status" do old_status = @book.status - @book.status = :published - assert @book.attribute_changed?(:status, from: old_status, to: 'published') + old_language = @book.language + @book.status = :proposed + @book.language = :french + assert @book.attribute_changed?(:status, from: old_status, to: 'proposed') + assert @book.attribute_changed?(:language, from: old_language, to: 'french') end test "enum didn't change" do @@ -141,7 +174,7 @@ class EnumTest < ActiveRecord::TestCase end test "persist changes that are dirty" do - @book.status = :published + @book.status = :proposed assert @book.attribute_changed?(:status) @book.status = :written assert @book.attribute_changed?(:status) @@ -149,7 +182,7 @@ class EnumTest < ActiveRecord::TestCase test "reverted changes that are not dirty" do old_status = @book.status - @book.status = :published + @book.status = :proposed assert @book.attribute_changed?(:status) @book.status = old_status assert_not @book.attribute_changed?(:status) @@ -201,18 +234,22 @@ class EnumTest < ActiveRecord::TestCase test "building new objects with enum scopes" do assert Book.written.build.written? assert Book.read.build.read? + assert Book.in_spanish.build.in_spanish? + assert Book.illustrator_visibility_invisible.build.illustrator_visibility_invisible? end test "creating new objects with enum scopes" do assert Book.written.create.written? assert Book.read.create.read? + assert Book.in_spanish.create.in_spanish? + assert Book.illustrator_visibility_invisible.create.illustrator_visibility_invisible? end test "_before_type_cast returns the enum label (required for form fields)" do if @book.status_came_from_user? - assert_equal "proposed", @book.status_before_type_cast + assert_equal "published", @book.status_before_type_cast else - assert_equal "proposed", @book.status + assert_equal "published", @book.status end end @@ -355,4 +392,17 @@ class EnumTest < ActiveRecord::TestCase book2 = klass.single.create! assert book2.single? end + + test "query state by predicate with prefix" do + assert @book.author_visibility_visible? + assert_not @book.author_visibility_invisible? + assert @book.illustrator_visibility_visible? + assert_not @book.illustrator_visibility_invisible? + end + + test "query state by predicate with custom prefix" do + assert @book.in_english? + assert_not @book.in_spanish? + assert_not @book.in_french? + end end diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb index 8de2ddb10d..2dee8a26a5 100644 --- a/activerecord/test/cases/explain_subscriber_test.rb +++ b/activerecord/test/cases/explain_subscriber_test.rb @@ -48,6 +48,11 @@ if ActiveRecord::Base.connection.supports_explain? assert queries.empty? end + def test_collects_cte_queries + SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'with s as (values(3)) select 1 from s') + assert_equal 1, queries.size + end + teardown do ActiveRecord::ExplainRegistry.reset end diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 97ba178b4d..03a187ae92 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -16,6 +16,7 @@ require 'models/joke' require 'models/matey' require 'models/parrot' require 'models/pirate' +require 'models/doubloon' require 'models/post' require 'models/randomly_named_c1' require 'models/reply' @@ -691,7 +692,7 @@ end class FoxyFixturesTest < ActiveRecord::TestCase fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, - :developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots + :developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots, :books if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' require 'models/uuid_parent' @@ -841,6 +842,13 @@ class FoxyFixturesTest < ActiveRecord::TestCase assert admin_accounts(:signals37).users.include?(admin_users(:david)) assert_equal 2, admin_accounts(:signals37).users.size end + + def test_resolves_enums + assert books(:awdr).published? + assert books(:awdr).read? + assert books(:rfr).proposed? + assert books(:ddd).published? + end end class ActiveSupportSubclassWithFixturesTest < ActiveRecord::TestCase @@ -896,3 +904,12 @@ class FixturesWithDefaultScopeTest < ActiveRecord::TestCase assert_equal "special", bulbs(:special).name end end + +class FixturesWithAbstractBelongsTo < ActiveRecord::TestCase + fixtures :pirates, :doubloons + + test "creates fixtures with belongs_to associations defined in abstract base classes" do + assert_not_nil doubloons(:blackbeards_doubloon) + assert_equal pirates(:blackbeard), doubloons(:blackbeards_doubloon).pirate + end +end diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index 8144f3e5c5..99230aa3d5 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -144,13 +144,17 @@ module ActiveRecord end def test_exception_on_removing_index_without_column_option - RemoveIndexMigration1.new.migrate(:up) - migration = RemoveIndexMigration2.new - migration.migrate(:up) + index_definition = ["horses", [:name, :color]] + migration1 = RemoveIndexMigration1.new + migration1.migrate(:up) + assert migration1.connection.index_exists?(*index_definition) - assert_raises(IrreversibleMigration) do - migration.migrate(:down) - end + migration2 = RemoveIndexMigration2.new + migration2.migrate(:up) + assert_not migration2.connection.index_exists?(*index_definition) + + migration2.migrate(:down) + assert migration2.connection.index_exists?(*index_definition) end def test_migrate_up diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 46a62c272f..83e50048ec 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -105,7 +105,7 @@ module ActiveRecord eight = columns.detect { |c| c.name == "eight_int" } if current_adapter?(:OracleAdapter) - assert_equal 'NUMBER(8)', eight.sql_type + assert_equal 'NUMBER(19)', eight.sql_type elsif current_adapter?(:SQLite3Adapter) assert_equal 'bigint', eight.sql_type else diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 3844b1a92e..90b7c6b38a 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -206,6 +206,11 @@ module ActiveRecord end def test_invert_remove_index + add = @recorder.inverse_of :remove_index, [:table, :one] + assert_equal [:add_index, [:table, :one]], add + end + + def test_invert_remove_index_with_column add = @recorder.inverse_of :remove_index, [:table, {column: [:one, :two], options: true}] assert_equal [:add_index, [:table, [:one, :two], options: true]], add end @@ -281,17 +286,42 @@ module ActiveRecord assert_equal [:remove_foreign_key, [:dogs, :people]], enable end + def test_invert_remove_foreign_key + enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people] + assert_equal [:add_foreign_key, [:dogs, :people]], enable + end + def test_invert_add_foreign_key_with_column enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id"] assert_equal [:remove_foreign_key, [:dogs, column: "owner_id"]], enable end + def test_invert_remove_foreign_key_with_column + enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id"] + assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id"]], enable + end + def test_invert_add_foreign_key_with_column_and_name enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"] assert_equal [:remove_foreign_key, [:dogs, name: "fk"]], enable end - def test_remove_foreign_key_is_irreversible + def test_invert_remove_foreign_key_with_column_and_name + enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"] + assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]], enable + end + + def test_invert_remove_foreign_key_with_primary_key + enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, primary_key: "person_id"] + assert_equal [:add_foreign_key, [:dogs, :people, primary_key: "person_id"]], enable + end + + def test_invert_remove_foreign_key_with_on_delete_on_update + enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade] + assert_equal [:add_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade]], enable + end + + def test_invert_remove_foreign_key_is_irreversible_without_to_table assert_raises ActiveRecord::IrreversibleMigration do @recorder.inverse_of :remove_foreign_key, [:dogs, column: "owner_id"] end @@ -299,6 +329,10 @@ module ActiveRecord assert_raises ActiveRecord::IrreversibleMigration do @recorder.inverse_of :remove_foreign_key, [:dogs, name: "fk"] end + + assert_raises ActiveRecord::IrreversibleMigration do + @recorder.inverse_of :remove_foreign_key, [:dogs] + end end end end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 1e93e2a05c..42e7507631 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -897,6 +897,33 @@ class PersistenceTest < ActiveRecord::TestCase assert_not post.new_record? end + def test_reload_via_querycache + ActiveRecord::Base.connection.enable_query_cache! + ActiveRecord::Base.connection.clear_query_cache + assert ActiveRecord::Base.connection.query_cache_enabled, 'cache should be on' + parrot = Parrot.create(:name => 'Shane') + + # populate the cache with the SELECT result + found_parrot = Parrot.find(parrot.id) + assert_equal parrot.id, found_parrot.id + + # Manually update the 'name' attribute in the DB directly + assert_equal 1, ActiveRecord::Base.connection.query_cache.length + ActiveRecord::Base.uncached do + found_parrot.name = 'Mary' + found_parrot.save + end + + # Now reload, and verify that it gets the DB version, and not the querycache version + found_parrot.reload + assert_equal 'Mary', found_parrot.name + + found_parrot = Parrot.find(parrot.id) + assert_equal 'Mary', found_parrot.name + ensure + ActiveRecord::Base.connection.disable_query_cache! + end + class SaveTest < ActiveRecord::TestCase self.use_transactional_tests = false diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index b8433f0bba..0745a37ee9 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -182,6 +182,12 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_equal "nextval('\"mixed_case_monkeys_monkeyID_seq\"'::regclass)", column.default_function assert column.serial? end + + def test_serial_with_unquoted_sequence_name + column = Topic.columns_hash[Topic.primary_key] + assert_equal "nextval('topics_id_seq'::regclass)", column.default_function + assert column.serial? + end end end diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb index 29c9d0e2af..989f4e1e5d 100644 --- a/activerecord/test/cases/relation/delegation_test.rb +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -28,7 +28,7 @@ module ActiveRecord module DelegationWhitelistBlacklistTests ARRAY_DELEGATES = [ :+, :-, :|, :&, :[], - :all?, :collect, :detect, :each, :each_cons, :each_with_index, + :all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index, :exclude?, :find_all, :flat_map, :group_by, :include?, :length, :map, :none?, :one?, :partition, :reject, :reverse, :sample, :second, :sort, :sort_by, :third, diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb index 45ead08bd5..ba4d9d2503 100644 --- a/activerecord/test/cases/relation/mutation_test.rb +++ b/activerecord/test/cases/relation/mutation_test.rb @@ -81,7 +81,7 @@ module ActiveRecord assert_equal [], relation.extending_values end - (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with]).each do |method| + (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :uniq]).each do |method| test "##{method}!" do assert relation.public_send("#{method}!", :foo).equal?(relation) assert_equal :foo, relation.public_send("#{method}_value") @@ -153,13 +153,22 @@ module ActiveRecord test 'distinct!' do relation.distinct! :foo assert_equal :foo, relation.distinct_value - assert_equal :foo, relation.uniq_value # deprecated access + + assert_deprecated do + assert_equal :foo, relation.uniq_value # deprecated access + end end test 'uniq! was replaced by distinct!' do - relation.uniq! :foo + assert_deprecated(/use distinct! instead/) do + relation.uniq! :foo + end + + assert_deprecated(/use distinct_value instead/) do + assert_equal :foo, relation.uniq_value # deprecated access + end + assert_equal :foo, relation.distinct_value - assert_equal :foo, relation.uniq_value # deprecated access end end end diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 9353be1ba7..37d3965022 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -242,6 +242,19 @@ module ActiveRecord assert_equal false, post.respond_to?(:title), "post should not respond_to?(:body) since invoking it raises exception" end + def test_select_quotes_when_using_from_clause + if sqlite3_version_includes_quoting_bug? + skip <<-ERROR.squish + You are using an outdated version of SQLite3 which has a bug in + quoted column names. Please update SQLite3 and rebuild the sqlite3 + ruby gem + ERROR + end + quoted_join = ActiveRecord::Base.connection.quote_table_name("join") + selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join) + assert_equal Post.pluck(:id), selected + end + def test_relation_merging_with_merged_joins_as_strings join_string = "LEFT OUTER JOIN #{Rating.quoted_table_name} ON #{SpecialComment.quoted_table_name}.id = #{Rating.quoted_table_name}.comment_id" special_comments_with_ratings = SpecialComment.joins join_string @@ -276,5 +289,16 @@ module ActiveRecord assert_equal "type cast from database", UpdateAllTestModel.first.body end + + private + + def sqlite3_version_includes_quoting_bug? + if current_adapter?(:SQLite3Adapter) + selected_quoted_column_names = ActiveRecord::Base.connection.exec_query( + 'SELECT "join" FROM (SELECT id AS "join" FROM posts) subquery' + ).columns + ["join"] != selected_quoted_column_names + end + end end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index b8e2041b6d..acbf85d398 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -17,7 +17,7 @@ require 'models/tyre' require 'models/minivan' require 'models/aircraft' require "models/possession" - +require "models/reader" class RelationTest < ActiveRecord::TestCase fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments, @@ -621,6 +621,32 @@ class RelationTest < ActiveRecord::TestCase assert_equal 1, query.to_a.size end + def test_preloading_with_associations_and_merges + post = Post.create! title: 'Uhuu', body: 'body' + reader = Reader.create! post_id: post.id, person_id: 1 + comment = Comment.create! post_id: post.id, body: 'body' + + assert !comment.respond_to?(:readers) + + post_rel = Post.preload(:readers).joins(:readers).where(title: 'Uhuu') + result_comment = Comment.joins(:post).merge(post_rel).to_a.first + assert_equal comment, result_comment + + assert_no_queries do + assert_equal post, result_comment.post + assert_equal [reader], result_comment.post.readers.to_a + end + + post_rel = Post.includes(:readers).where(title: 'Uhuu') + result_comment = Comment.joins(:post).merge(post_rel).first + assert_equal comment, result_comment + + assert_no_queries do + assert_equal post, result_comment.post + assert_equal [reader], result_comment.post.readers.to_a + end + end + def test_loading_with_one_association posts = Post.preload(:comments) post = posts.find { |p| p.id == 1 } @@ -908,7 +934,7 @@ class RelationTest < ActiveRecord::TestCase def test_delete_all_with_unpermitted_relation_raises_error assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all } - assert_raises(ActiveRecord::ActiveRecordError) { Author.uniq.delete_all } + assert_raises(ActiveRecord::ActiveRecordError) { Author.distinct.delete_all } assert_raises(ActiveRecord::ActiveRecordError) { Author.group(:name).delete_all } assert_raises(ActiveRecord::ActiveRecordError) { Author.having('SUM(id) < 3').delete_all } assert_raises(ActiveRecord::ActiveRecordError) { Author.offset(10).delete_all } @@ -1493,14 +1519,17 @@ class RelationTest < ActiveRecord::TestCase assert_equal ['Foo', 'Foo'], query.map(&:name) assert_sql(/DISTINCT/) do assert_equal ['Foo'], query.distinct.map(&:name) - assert_equal ['Foo'], query.uniq.map(&:name) + assert_deprecated { assert_equal ['Foo'], query.uniq.map(&:name) } end assert_sql(/DISTINCT/) do assert_equal ['Foo'], query.distinct(true).map(&:name) - assert_equal ['Foo'], query.uniq(true).map(&:name) + assert_deprecated { assert_equal ['Foo'], query.uniq(true).map(&:name) } end assert_equal ['Foo', 'Foo'], query.distinct(true).distinct(false).map(&:name) - assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name) + + assert_deprecated do + assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name) + end end def test_doesnt_add_having_values_if_options_are_blank diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index e6f0fe6f75..51170c533b 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -253,6 +253,11 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{t\.integer\s+"big_int_data_points\",\s+limit: 8,\s+array: true}, output end + def test_schema_dump_allows_array_of_decimal_defaults + output = standard_dump + assert_match %r{t\.decimal\s+"decimal_array_default",\s+default: \[1.23, 3.45\],\s+array: true}, output + end + if ActiveRecord::Base.connection.supports_extensions? def test_schema_dump_includes_extensions connection = ActiveRecord::Base.connection diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index 7c92453ee3..6056156698 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -274,4 +274,25 @@ class SerializedAttributeTest < ActiveRecord::TestCase assert_equal({}, topic.content) end + + def test_values_cast_from_nil_are_persisted_as_nil + # This is required to fulfil the following contract, which must be universally + # true in Active Record: + # + # model.attribute = value + # assert_equal model.attribute, model.tap(&:save).reload.attribute + Topic.serialize(:content, Hash) + topic = Topic.create!(content: {}) + topic2 = Topic.create!(content: nil) + + assert_equal [topic, topic2], Topic.where(content: nil) + end + + def test_nil_is_always_persisted_as_null + Topic.serialize(:content, Hash) + + topic = Topic.create!(content: { foo: "bar" }) + topic.update_attribute :content, nil + assert_equal [topic], Topic.where(content: nil) + end end diff --git a/activerecord/test/cases/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb index 1c449d42fe..72c5c16555 100644 --- a/activerecord/test/cases/suppressor_test.rb +++ b/activerecord/test/cases/suppressor_test.rb @@ -3,7 +3,38 @@ require 'models/notification' require 'models/user' class SuppressorTest < ActiveRecord::TestCase - def test_suppresses_creation_of_record_generated_by_callback + def test_suppresses_create + assert_no_difference -> { Notification.count } do + Notification.suppress do + Notification.create + Notification.create! + Notification.new.save + Notification.new.save! + end + end + end + + def test_suppresses_update + user = User.create! token: 'asdf' + + User.suppress do + user.update token: 'ghjkl' + assert_equal 'asdf', user.reload.token + + user.update! token: 'zxcvbnm' + assert_equal 'asdf', user.reload.token + + user.token = 'qwerty' + user.save + assert_equal 'asdf', user.reload.token + + user.token = 'uiop' + user.save! + assert_equal 'asdf', user.reload.token + end + end + + def test_suppresses_create_in_callback assert_difference -> { User.count } do assert_no_difference -> { Notification.count } do Notification.suppress { UserWithNotification.create! } diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index 8d69741a4a..d0deb4c273 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -265,14 +265,14 @@ module ActiveRecord def test_structure_dump filename = "awesome-file.sql" - Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(true) + Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) end def test_warn_when_external_structure_dump_fails filename = "awesome-file.sql" - Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(false) + Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(false) warnings = capture(:stderr) do ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) @@ -283,7 +283,7 @@ module ActiveRecord def test_structure_dump_with_port_number filename = "awesome-file.sql" - Kernel.expects(:system).with("mysqldump", "--port=10000", "--result-file", filename, "--no-data", "test-db").returns(true) + Kernel.expects(:system).with("mysqldump", "--port=10000", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_dump( @configuration.merge('port' => 10000), @@ -292,7 +292,7 @@ module ActiveRecord def test_structure_dump_with_ssl filename = "awesome-file.sql" - Kernel.expects(:system).with("mysqldump", "--ssl-ca=ca.crt", "--result-file", filename, "--no-data", "test-db").returns(true) + Kernel.expects(:system).with("mysqldump", "--ssl-ca=ca.crt", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_dump( @configuration.merge("sslca" => "ca.crt"), diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index e0b01ae8e0..7761ea5612 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -65,6 +65,30 @@ module ActiveRecord end end + class PostgreSQLTestCase < TestCase + def self.run(*args) + super if current_adapter?(:PostgreSQLAdapter) + end + end + + class Mysql2TestCase < TestCase + def self.run(*args) + super if current_adapter?(:Mysql2Adapter) + end + end + + class MysqlTestCase < TestCase + def self.run(*args) + super if current_adapter?(:MysqlAdapter) + end + end + + class SQLite3TestCase < TestCase + def self.run(*args) + super if current_adapter?(:SQLite3Adapter) + end + end + class SQLCounter class << self attr_accessor :ignored_sql, :log, :log_all @@ -81,7 +105,7 @@ module ActiveRecord oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im] mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /] postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] - sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im] + sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im] [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql| ignored_sql.concat db_ignored_sql diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb index 3aed90ba36..f9dca1e196 100644 --- a/activerecord/test/cases/view_test.rb +++ b/activerecord/test/cases/view_test.rb @@ -102,7 +102,7 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase end def test_attributes - assert_equal({"name" => "Agile Web Development with Rails", "status" => 0}, + assert_equal({"name" => "Agile Web Development with Rails", "status" => 2}, Paperback.first.attributes) end diff --git a/activerecord/test/fixtures/books.yml b/activerecord/test/fixtures/books.yml index abe56752c6..93cfabd61c 100644 --- a/activerecord/test/fixtures/books.yml +++ b/activerecord/test/fixtures/books.yml @@ -3,9 +3,24 @@ awdr: id: 1 name: "Agile Web Development with Rails" format: "paperback" + status: :published + read_status: :read + language: :english + author_visibility: :visible + illustrator_visibility: :visible + font_size: :medium rfr: author_id: 1 id: 2 name: "Ruby for Rails" format: "ebook" + status: "proposed" + read_status: "reading" + +ddd: + author_id: 1 + id: 3 + name: "Domain-Driven Design" + format: "hardcover" + status: 2 diff --git a/activerecord/test/fixtures/doubloons.yml b/activerecord/test/fixtures/doubloons.yml new file mode 100644 index 0000000000..efd1643971 --- /dev/null +++ b/activerecord/test/fixtures/doubloons.yml @@ -0,0 +1,3 @@ +blackbeards_doubloon: + pirate: blackbeard + weight: 2 diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb index 2170018068..24bfe47bbf 100644 --- a/activerecord/test/models/book.rb +++ b/activerecord/test/models/book.rb @@ -10,6 +10,10 @@ class Book < ActiveRecord::Base enum status: [:proposed, :written, :published] enum read_status: {unread: 0, reading: 2, read: 3} enum nullable_status: [:single, :married] + enum language: [:english, :spanish, :french], enum_prefix: :in + enum author_visibility: [:visible, :invisible], enum_prefix: true + enum illustrator_visibility: [:visible, :invisible], enum_prefix: true + enum font_size: [:small, :medium, :large], enum_prefix: :with, enum_suffix: true def published! super diff --git a/activerecord/test/models/doubloon.rb b/activerecord/test/models/doubloon.rb new file mode 100644 index 0000000000..2b11d128e2 --- /dev/null +++ b/activerecord/test/models/doubloon.rb @@ -0,0 +1,12 @@ +class AbstractDoubloon < ActiveRecord::Base + # This has functionality that might be shared by multiple classes. + + self.abstract_class = true + belongs_to :pirate +end + +class Doubloon < AbstractDoubloon + # This uses an abstract class that defines attributes and associations. + + self.table_name = 'doubloons' +end diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index 872fa595b4..df0362573b 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -107,5 +107,6 @@ _SQL create_table :bigint_array, force: true do |t| t.integer :big_int_data_points, limit: 8, array: true + t.decimal :decimal_array_default, array: true, default: [1.23, 3.45] end end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 66f8f1611d..dffccc9326 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -100,6 +100,10 @@ ActiveRecord::Schema.define do t.column :status, :integer, default: 0 t.column :read_status, :integer, default: 0 t.column :nullable_status, :integer + t.column :language, :integer, default: 0 + t.column :author_visibility, :integer, default: 0 + t.column :illustrator_visibility, :integer, default: 0 + t.column :font_size, :integer, default: 0 end create_table :booleans, force: true do |t| @@ -266,6 +270,11 @@ ActiveRecord::Schema.define do t.string :alias end + create_table :doubloons, force: true do |t| + t.integer :pirate_id + t.integer :weight + end + create_table :edges, force: true, id: false do |t| t.column :source_id, :integer, null: false t.column :sink_id, :integer, null: false |