From b7f15a2b94f7697da63228b1597be75e61816266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 28 Jan 2022 22:00:32 +0000 Subject: [PATCH] Dump the database schema containing the current Rails version Since https://github.com/rails/rails/pull/42297, Rails now generates datetime columns on MySQL with a default precision of 6. This means that users upgrading to Rails 7.0 from 6.1, when loading the database schema, would get the new precision value, which would not match the production schema. To avoid problems like this in the future, Rails will now freeze `ActiveRecord::Schema` class to be the 7.0 implementation, and will allow access to other version using the same mechanism of `ActiveRecord::Migration`. The schema dumper will generate the new format which will include the Rails version and will look like this: ``` ActiveRecord::Schema[7.0].define ``` Related to #43934 and #43909. --- .../lib/active_record/railties/databases.rake | 4 +- activerecord/lib/active_record/schema.rb | 66 ++++++++++++------- .../lib/active_record/schema_dumper.rb | 32 ++++----- ...a_test.rb => active_record_schema_test.rb} | 46 +++++++++---- .../cases/adapters/postgresql/enum_test.rb | 2 +- activerecord/test/cases/migrator_test.rb | 2 +- activerecord/test/cases/schema_dumper_test.rb | 5 ++ .../test/schema/mysql2_specific_schema.rb | 2 +- .../test/schema/oracle_specific_schema.rb | 2 +- .../test/schema/postgresql_specific_schema.rb | 2 +- activerecord/test/schema/schema.rb | 2 +- .../test/schema/sqlite_specific_schema.rb | 2 +- .../action_mailbox_gem.rb | 2 +- .../action_mailbox_main.rb | 2 +- .../bug_report_templates/active_record_gem.rb | 2 +- .../active_record_main.rb | 2 +- .../active_record_migrations_gem.rb | 2 +- .../active_record_migrations_main.rb | 2 +- .../active_storage_gem.rb | 2 +- .../active_storage_main.rb | 2 +- guides/source/active_record_migrations.md | 2 +- guides/source/active_record_postgresql.md | 2 +- guides/source/upgrading_ruby_on_rails.md | 26 ++++++++ .../test/application/configuration_test.rb | 8 +-- railties/test/application/loading_test.rb | 2 +- railties/test/application/rake/dbs_test.rb | 6 +- railties/test/application/test_runner_test.rb | 6 +- railties/test/application/test_test.rb | 8 +-- 28 files changed, 156 insertions(+), 87 deletions(-) rename activerecord/test/cases/{ar_schema_test.rb => active_record_schema_test.rb} (84%) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index bae25bf46ae4f..db34801949aa3 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -548,7 +548,7 @@ db_namespace = namespace :db do # desc "Recreate the test database from an existent schema file (schema.rb or structure.sql, depending on `config.active_record.schema_format`)" task load_schema: %w(db:test:purge) do should_reconnect = ActiveRecord::Base.connection_pool.active_connection? - ActiveRecord::Schema.verbose = false + ActiveRecord::Schema::Current.verbose = false ActiveRecord::Base.configurations.configs_for(env_name: "test").each do |db_config| ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config) end @@ -584,7 +584,7 @@ db_namespace = namespace :db do namespace :load_schema do task name => "db:test:purge:#{name}" do should_reconnect = ActiveRecord::Base.connection_pool.active_connection? - ActiveRecord::Schema.verbose = false + ActiveRecord::Schema::Current.verbose = false db_config = ActiveRecord::Base.configurations.configs_for(env_name: "test", name: name) ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config) ensure diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index aba25fb3757ca..2d8928f7be287 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -10,7 +10,7 @@ module ActiveRecord # # Usage: # - # ActiveRecord::Schema.define do + # ActiveRecord::Schema[7.0].define do # create_table :authors do |t| # t.string :name, null: false # end @@ -29,33 +29,51 @@ module ActiveRecord # # ActiveRecord::Schema is only supported by database adapters that also # support migrations, the two features being very similar. - class Schema < Migration::Current - # Eval the given block. All methods available to the current connection - # adapter are available within the block, so you can easily use the - # database definition DSL to build up your schema ( - # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table], - # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.). - # - # The +info+ hash is optional, and if given is used to define metadata - # about the current schema (currently, only the schema's version): - # - # ActiveRecord::Schema.define(version: 2038_01_19_000001) do - # ... - # end - def self.define(info = {}, &block) - new.define(info, &block) - end + class Schema < Migration[7.0] + module Definition + extend ActiveSupport::Concern + + module ClassMethods + # Eval the given block. All methods available to the current connection + # adapter are available within the block, so you can easily use the + # database definition DSL to build up your schema ( + # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table], + # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.). + # + # The +info+ hash is optional, and if given is used to define metadata + # about the current schema (currently, only the schema's version): + # + # ActiveRecord::Schema[7.0].define(version: 2038_01_19_000001) do + # ... + # end + def define(info = {}, &block) + new.define(info, &block) + end + end - def define(info, &block) # :nodoc: - instance_eval(&block) + def define(info, &block) # :nodoc: + instance_eval(&block) - if info[:version].present? - connection.schema_migration.create_table - connection.assume_migrated_upto_version(info[:version]) + if info[:version].present? + connection.schema_migration.create_table + connection.assume_migrated_upto_version(info[:version]) + end + + ActiveRecord::InternalMetadata.create_table + ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment end + end - ActiveRecord::InternalMetadata.create_table - ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment + include Definition + + class Current < Migration::Current + include Definition + end + + def self.[](version) + Class.new(Migration::Compatibility.find(version)) do + include Definition + end end end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index c97d0899c8313..05aa4d43b40ca 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -73,23 +73,23 @@ def define_params @version ? "version: #{formatted_version}" : "" end + # TODO: Fix dumper def header(stream) - stream.puts <
{ "CURRENT_TIMESTAMP" } diff --git a/activerecord/test/schema/oracle_specific_schema.rb b/activerecord/test/schema/oracle_specific_schema.rb index 08c6e24555534..7e99c03ddfb4e 100644 --- a/activerecord/test/schema/oracle_specific_schema.rb +++ b/activerecord/test/schema/oracle_specific_schema.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do execute "drop table test_oracle_defaults" rescue nil execute "drop sequence test_oracle_defaults_seq" rescue nil execute "drop sequence companies_nonstd_seq" rescue nil diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index e058e8b157ed5..2684cd70fe0ad 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do enable_extension!("uuid-ossp", ActiveRecord::Base.connection) enable_extension!("pgcrypto", ActiveRecord::Base.connection) if ActiveRecord::Base.connection.supports_pgcrypto_uuid? diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 89c3f85304f03..a31769ba2b056 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do # ------------------------------------------------------------------- # # # # Please keep these create table statements in alphabetical order # diff --git a/activerecord/test/schema/sqlite_specific_schema.rb b/activerecord/test/schema/sqlite_specific_schema.rb index 5024769e0d863..e6d82c25f7a2e 100644 --- a/activerecord/test/schema/sqlite_specific_schema.rb +++ b/activerecord/test/schema/sqlite_specific_schema.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do create_table :defaults, force: true do |t| t.date :modified_date, default: -> { "CURRENT_DATE" } t.date :fixed_date, default: "2004-01-01" diff --git a/guides/bug_report_templates/action_mailbox_gem.rb b/guides/bug_report_templates/action_mailbox_gem.rb index a498d5d3eaee8..8ae6972fbd313 100644 --- a/guides/bug_report_templates/action_mailbox_gem.rb +++ b/guides/bug_report_templates/action_mailbox_gem.rb @@ -45,7 +45,7 @@ class TestApp < Rails::Application require ActiveStorage::Engine.root.join("db/migrate/20170806125915_create_active_storage_tables.rb").to_s require ActionMailbox::Engine.root.join("db/migrate/20180917164000_create_action_mailbox_tables.rb").to_s -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do CreateActiveStorageTables.new.change CreateActionMailboxTables.new.change end diff --git a/guides/bug_report_templates/action_mailbox_main.rb b/guides/bug_report_templates/action_mailbox_main.rb index 15d48c83209c8..c920d80855bfa 100644 --- a/guides/bug_report_templates/action_mailbox_main.rb +++ b/guides/bug_report_templates/action_mailbox_main.rb @@ -44,7 +44,7 @@ class TestApp < Rails::Application require ActiveStorage::Engine.root.join("db/migrate/20170806125915_create_active_storage_tables.rb").to_s require ActionMailbox::Engine.root.join("db/migrate/20180917164000_create_action_mailbox_tables.rb").to_s -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do CreateActiveStorageTables.new.change CreateActionMailboxTables.new.change end diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index 58b8727407cb1..a1816e9b2ba0a 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -20,7 +20,7 @@ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do create_table :posts, force: true do |t| end diff --git a/guides/bug_report_templates/active_record_main.rb b/guides/bug_report_templates/active_record_main.rb index f8acbabfa3b2f..dd777dd7ec246 100644 --- a/guides/bug_report_templates/active_record_main.rb +++ b/guides/bug_report_templates/active_record_main.rb @@ -19,7 +19,7 @@ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do create_table :posts, force: true do |t| end diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb index 513d900c9ddc0..55673918de09e 100644 --- a/guides/bug_report_templates/active_record_migrations_gem.rb +++ b/guides/bug_report_templates/active_record_migrations_gem.rb @@ -20,7 +20,7 @@ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do create_table :payments, force: true do |t| t.decimal :amount, precision: 10, scale: 0, default: 0, null: false end diff --git a/guides/bug_report_templates/active_record_migrations_main.rb b/guides/bug_report_templates/active_record_migrations_main.rb index 5836db3b7e215..cebb99a0b9ed7 100644 --- a/guides/bug_report_templates/active_record_migrations_main.rb +++ b/guides/bug_report_templates/active_record_migrations_main.rb @@ -19,7 +19,7 @@ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do create_table :payments, force: true do |t| t.decimal :amount, precision: 10, scale: 0, default: 0, null: false end diff --git a/guides/bug_report_templates/active_storage_gem.rb b/guides/bug_report_templates/active_storage_gem.rb index e9bcfffd028af..6a96737234ff5 100644 --- a/guides/bug_report_templates/active_storage_gem.rb +++ b/guides/bug_report_templates/active_storage_gem.rb @@ -41,7 +41,7 @@ class TestApp < Rails::Application require ActiveStorage::Engine.root.join("db/migrate/20170806125915_create_active_storage_tables.rb").to_s -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do CreateActiveStorageTables.new.change create_table :users, force: true diff --git a/guides/bug_report_templates/active_storage_main.rb b/guides/bug_report_templates/active_storage_main.rb index 942726490d270..f4b4aa54cd4da 100644 --- a/guides/bug_report_templates/active_storage_main.rb +++ b/guides/bug_report_templates/active_storage_main.rb @@ -40,7 +40,7 @@ class TestApp < Rails::Application require ActiveStorage::Engine.root.join("db/migrate/20170806125915_create_active_storage_tables.rb").to_s -ActiveRecord::Schema.define do +ActiveRecord::Schema::Current.define do CreateActiveStorageTables.new.change create_table :users, force: true diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index e53f460f8e82e..66a06d93d1484 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -1038,7 +1038,7 @@ If `:ruby` is selected, then the schema is stored in `db/schema.rb`. If you look at this file you'll find that it looks an awful lot like one very big migration: ```ruby -ActiveRecord::Schema.define(version: 2008_09_06_171750) do +ActiveRecord::Schema[7.0].define(version: 2008_09_06_171750) do create_table "authors", force: true do |t| t.string "name" t.datetime "created_at" diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index bef77a3918cc1..181636d66a2a7 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -98,7 +98,7 @@ NOTE: You need to enable the `hstore` extension to use hstore. ```ruby # db/migrate/20131009135255_create_profiles.rb -ActiveRecord::Schema.define do +class CreateProfiles < ActiveRecord::Migration[7.0] enable_extension 'hstore' unless extension_enabled?('hstore') create_table :profiles do |t| t.hstore 'settings' diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 3e84c1a4915b5..d178713861618 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -388,6 +388,32 @@ You can invalidate the cache either by touching the product, or changing the cac <% end %> ``` +### Rails version is now included in the Active Record schema dump + +Rails 7.0 changed some default values for some column types. To avoid that application upgrading from 6.1 to 7.0 +load the current schema using the new 7.0 defaults, Rails now includes the version of the framework in the schema dump. + +This means that after the upgrade, as soon the schema is loaded, you will see many changes to that file, including +some column information. Make sure to review the new schema file content and commit it to your repository. + +The schema file will look like this: + +```ruby +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.0].define(version: 2022_01_28_123512) do +``` + Upgrading from Rails 6.0 to Rails 6.1 ------------------------------------- diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index a09c18d602983..e77c728487a32 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -360,7 +360,7 @@ class Post < ActiveRecord::Base app_file "config/initializers/active_record.rb", <<-RUBY ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Migration.verbose = false - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :posts do |t| t.string :title end @@ -381,7 +381,7 @@ class Post < ActiveRecord::Base app_file "config/initializers/active_record.rb", <<-RUBY ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Migration.verbose = false - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :posts do |t| t.string :title end @@ -407,7 +407,7 @@ class Post < ActiveRecord::Base app_file "config/initializers/active_record.rb", <<-RUBY ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Migration.verbose = false - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :posts do |t| t.string :title end @@ -437,7 +437,7 @@ class Post < ActiveRecord::Base app_file "config/initializers/active_record.rb", <<-RUBY ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Migration.verbose = false - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :posts do |t| t.string :title end diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index 287196d20acf7..d59004deb9a11 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -486,7 +486,7 @@ def show def setup_ar! ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Migration.verbose = false - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :posts do |t| t.string :title end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index e4a9340096170..8b8c0415c9e26 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -475,7 +475,7 @@ def db_fixtures_load(expected_database) rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do + ActiveRecord::Schema::Current.define(version: 20140423102712) do create_table(:comments) {} end RUBY @@ -505,7 +505,7 @@ def db_fixtures_load(expected_database) ActiveRecord::Base.primary_key_prefix_type = :table_name RUBY app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do + ActiveRecord::Schema::Current.define(version: 20140423102712) do create_table("goose".pluralize) do |t| t.string :name end @@ -533,7 +533,7 @@ def db_fixtures_load(expected_database) ENV.delete "RACK_ENV" app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: "1") do + ActiveRecord::Schema::Current.define(version: "1") do create_table :users do |t| t.string :name end diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 9ed87aa39e178..91a6782721bf0 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -557,7 +557,7 @@ def test_run_in_parallel_with_processes file_name = create_parallel_processes_test_file app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :users do |t| t.string :name end @@ -577,7 +577,7 @@ def test_parallelization_is_disabled_when_number_of_tests_is_below_threshold file_name = create_parallel_processes_test_file app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :users do |t| t.string :name end @@ -636,7 +636,7 @@ def test_run_in_parallel_with_threads file_name = create_parallel_threads_test_file app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 1) do + ActiveRecord::Schema::Current.define(version: 1) do create_table :users do |t| t.string :name end diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb index 227a8cc6e865e..7419f6ba70f20 100644 --- a/railties/test/application/test_test.rb +++ b/railties/test/application/test_test.rb @@ -124,7 +124,7 @@ class UserTest < ActiveSupport::TestCase assert_unsuccessful_run "models/user_test.rb", "Migrations are pending" app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: #{version}) do + ActiveRecord::Schema::Current.define(version: #{version}) do create_table :users do |t| t.string :name end @@ -246,7 +246,7 @@ class UserTest < ActiveSupport::TestCase end RUBY app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: #{version}) do + ActiveRecord::Schema::Current.define(version: #{version}) do create_table :users do |t| t.string :name end @@ -257,7 +257,7 @@ class UserTest < ActiveSupport::TestCase # Simulate `db:rollback` + edit of the migration file + `db:migrate` app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: #{version}) do + ActiveRecord::Schema::Current.define(version: #{version}) do create_table :users do |t| t.string :name t.integer :age @@ -301,7 +301,7 @@ class UserTest < ActiveSupport::TestCase # Simulate `db:migrate` app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: #{version}) do + ActiveRecord::Schema::Current.define(version: #{version}) do create_table :users do |t| t.string :name end