diff options
Diffstat (limited to 'activerecord/lib/active_record/railties')
4 files changed, 450 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/railties/console_sandbox.rb b/activerecord/lib/active_record/railties/console_sandbox.rb new file mode 100644 index 0000000000..8917638a5d --- /dev/null +++ b/activerecord/lib/active_record/railties/console_sandbox.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +ActiveRecord::Base.connection.begin_transaction(joinable: false) + +at_exit do + ActiveRecord::Base.connection.rollback_transaction +end diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb new file mode 100644 index 0000000000..3cf66980a5 --- /dev/null +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "active_support/core_ext/module/attr_internal" +require_relative "../log_subscriber" + +module ActiveRecord + module Railties # :nodoc: + module ControllerRuntime #:nodoc: + extend ActiveSupport::Concern + + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. + protected + + attr_internal :db_runtime + + private + + def process_action(action, *args) + # We also need to reset the runtime before each action + # because of queries in middleware or in cases we are streaming + # and it won't be cleaned up by the method below. + ActiveRecord::LogSubscriber.reset_runtime + super + end + + def cleanup_view_runtime + if logger && logger.info? && ActiveRecord::Base.connected? + db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime + self.db_runtime = (db_runtime || 0) + db_rt_before_render + runtime = super + db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime + self.db_runtime += db_rt_after_render + runtime - db_rt_after_render + else + super + end + end + + def append_info_to_payload(payload) + super + if ActiveRecord::Base.connected? + payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime + end + end + + module ClassMethods # :nodoc: + def log_process_action(payload) + messages, db_runtime = super, payload[:db_runtime] + messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime + messages + end + end + end + end +end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake new file mode 100644 index 0000000000..691b3612d8 --- /dev/null +++ b/activerecord/lib/active_record/railties/databases.rake @@ -0,0 +1,369 @@ +# frozen_string_literal: true + +require "active_record" + +db_namespace = namespace :db do + desc "Set the environment value for the database" + task "environment:set" => [:environment, :load_config] do + ActiveRecord::InternalMetadata.create_table + ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment + end + + task check_protected_environments: [:environment, :load_config] do + ActiveRecord::Tasks::DatabaseTasks.check_protected_environments! + end + + task :load_config do + ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {} + ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths + end + + namespace :create do + task all: :load_config do + ActiveRecord::Tasks::DatabaseTasks.create_all + 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 or when RAILS_ENV is development, it defaults to creating the development and test databases." + task create: [:load_config] do + ActiveRecord::Tasks::DatabaseTasks.create_current + end + + namespace :drop do + task all: [:load_config, :check_protected_environments] do + ActiveRecord::Tasks::DatabaseTasks.drop_all + 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 or when RAILS_ENV is development, it defaults to dropping the development and test databases." + task drop: [:load_config, :check_protected_environments] do + db_namespace["drop:_unsafe"].invoke + end + + task "drop:_unsafe" => [:load_config] do + ActiveRecord::Tasks::DatabaseTasks.drop_current + end + + namespace :purge do + task all: [:load_config, :check_protected_environments] do + ActiveRecord::Tasks::DatabaseTasks.purge_all + end + end + + # desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:purge:all to purge all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases." + task purge: [:load_config, :check_protected_environments] do + ActiveRecord::Tasks::DatabaseTasks.purge_current + end + + desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)." + task migrate: [:environment, :load_config] do + ActiveRecord::Tasks::DatabaseTasks.migrate + db_namespace["_dump"].invoke + end + + # IMPORTANT: This task won't dump the schema if ActiveRecord::Base.dump_schema_after_migration is set to false + task :_dump do + if ActiveRecord::Base.dump_schema_after_migration + case ActiveRecord::Base.schema_format + when :ruby then db_namespace["schema:dump"].invoke + when :sql then db_namespace["structure:dump"].invoke + else + raise "unknown schema format #{ActiveRecord::Base.schema_format}" + end + end + # Allow this task to be called as many times as required. An example is the + # migrate:redo task, which calls other two internally that depend on this one. + db_namespace["_dump"].reenable + end + + namespace :migrate do + # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).' + task redo: [:environment, :load_config] do + raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty? + + if ENV["VERSION"] + db_namespace["migrate:down"].invoke + db_namespace["migrate:up"].invoke + else + db_namespace["rollback"].invoke + db_namespace["migrate"].invoke + end + end + + # desc 'Resets your database using your migrations for the current environment' + task reset: ["db:drop", "db:create", "db:migrate"] + + # desc 'Runs the "up" for a given migration VERSION.' + task up: [:environment, :load_config] do + raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty? + + version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil + ActiveRecord::Migrator.run(:up, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version) + db_namespace["_dump"].invoke + end + + # desc 'Runs the "down" for a given migration VERSION.' + task down: [:environment, :load_config] do + raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty? + version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil + ActiveRecord::Migrator.run(:down, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version) + db_namespace["_dump"].invoke + end + + desc "Display status of migrations" + task status: [:environment, :load_config] do + unless ActiveRecord::SchemaMigration.table_exists? + abort "Schema migrations table does not exist yet." + end + + # output + puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n" + puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name" + puts "-" * 50 + paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths + ActiveRecord::Migrator.migrations_status(paths).each do |status, version, name| + puts "#{status.center(8)} #{version.ljust(14)} #{name}" + end + puts + end + end + + desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)." + task rollback: [:environment, :load_config] do + step = ENV["STEP"] ? ENV["STEP"].to_i : 1 + ActiveRecord::Migrator.rollback(ActiveRecord::Tasks::DatabaseTasks.migrations_paths, step) + db_namespace["_dump"].invoke + end + + # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' + task forward: [:environment, :load_config] do + step = ENV["STEP"] ? ENV["STEP"].to_i : 1 + ActiveRecord::Migrator.forward(ActiveRecord::Tasks::DatabaseTasks.migrations_paths, step) + db_namespace["_dump"].invoke + end + + # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' + task reset: [ "db:drop", "db:setup" ] + + # desc "Retrieves the charset for the current environment's database" + task charset: [:environment, :load_config] do + puts ActiveRecord::Tasks::DatabaseTasks.charset_current + end + + # desc "Retrieves the collation for the current environment's database" + task collation: [:environment, :load_config] do + begin + puts ActiveRecord::Tasks::DatabaseTasks.collation_current + rescue NoMethodError + $stderr.puts "Sorry, your database adapter is not supported yet. Feel free to submit a patch." + end + end + + desc "Retrieves the current schema version number" + task version: [:environment, :load_config] do + puts "Current version: #{ActiveRecord::Migrator.current_version}" + end + + # desc "Raises an error if there are pending migrations" + task abort_if_pending_migrations: [:environment, :load_config] do + pending_migrations = ActiveRecord::Migrator.open(ActiveRecord::Tasks::DatabaseTasks.migrations_paths).pending_migrations + + if pending_migrations.any? + puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}" + pending_migrations.each do |pending_migration| + puts " %4d %s" % [pending_migration.version, pending_migration.name] + end + abort %{Run `rails db:migrate` to update your database then try again.} + end + end + + 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 "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 "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_relative "../fixtures" + + base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path + + fixtures_dir = if ENV["FIXTURES_DIR"] + File.join base_dir, ENV["FIXTURES_DIR"] + else + base_dir + end + + fixture_files = if ENV["FIXTURES"] + ENV["FIXTURES"].split(",") + else + # The use of String#[] here is to support namespaced fixtures. + Dir["#{fixtures_dir}/**/*.yml"].map { |f| f[(fixtures_dir.size + 1)..-5] } + end + + ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files) + end + + # desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." + task identify: [:environment, :load_config] do + require_relative "../fixtures" + + label, id = ENV["LABEL"], ENV["ID"] + raise "LABEL or ID required" if label.blank? && id.blank? + + puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label + + base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path + + Dir["#{base_dir}/**/*.yml"].each do |file| + if data = YAML::load(ERB.new(IO.read(file)).result) + data.each_key do |key| + key_id = ActiveRecord::FixtureSet.identify(key) + + if key == label || key_id == id.to_i + puts "#{file}: #{key} (#{key_id})" + end + end + end + end + end + end + + namespace :schema do + desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record" + task dump: [:environment, :load_config] do + require_relative "../schema_dumper" + filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema.rb") + File.open(filename, "w:utf-8") do |file| + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) + end + db_namespace["schema:dump"].reenable + end + + desc "Loads a schema.rb file into the database" + task load: [:environment, :load_config, :check_protected_environments] do + ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV["SCHEMA"]) + end + + task load_if_ruby: ["db:create", :environment] do + db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby + end + + namespace :cache do + desc "Creates a db/schema_cache.yml file." + task dump: [:environment, :load_config] do + conn = ActiveRecord::Base.connection + filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml") + ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(conn, filename) + end + + desc "Clears a db/schema_cache.yml file." + task clear: [:environment, :load_config] do + filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml") + rm_f filename, verbose: false + end + end + + end + + namespace :structure do + 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 + ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename) + + if ActiveRecord::SchemaMigration.table_exists? + File.open(filename, "a") do |f| + f.puts ActiveRecord::Base.connection.dump_schema_information + f.print "\n" + end + end + db_namespace["structure:dump"].reenable + end + + desc "Recreates the databases from the structure.sql file" + task load: [:environment, :load_config, :check_protected_environments] do + ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV["SCHEMA"]) + end + + task load_if_sql: ["db:create", :environment] do + db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql + end + end + + namespace :test do + # desc "Recreate the test database from the current schema" + task load: %w(db:test:purge) do + case ActiveRecord::Base.schema_format + when :ruby + db_namespace["test:load_schema"].invoke + when :sql + db_namespace["test:load_structure"].invoke + end + end + + # desc "Recreate the test database from an existent schema.rb file" + task load_schema: %w(db:test:purge) do + begin + should_reconnect = ActiveRecord::Base.connection_pool.active_connection? + ActiveRecord::Schema.verbose = false + ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :ruby, ENV["SCHEMA"] + ensure + if should_reconnect + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env]) + end + end + end + + # desc "Recreate the test database from an existent structure.sql file" + task load_structure: %w(db:test:purge) do + ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"] + end + + # desc "Empty the test database" + task purge: %w(environment load_config check_protected_environments) do + ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations["test"] + end + + # desc 'Load the test schema' + task prepare: %w(environment load_config) do + unless ActiveRecord::Base.configurations.blank? + db_namespace["test:load"].invoke + end + end + end +end + +namespace :railties do + namespace :install do + # desc "Copies missing migrations from Railties (e.g. engines). You can specify Railties to use with FROM=railtie1,railtie2" + task migrations: :'db:load_config' do + to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map(&:strip) + railties = {} + Rails.application.migration_railties.each do |railtie| + next unless to_load == :all || to_load.include?(railtie.railtie_name) + + if railtie.respond_to?(:paths) && (path = railtie.paths["db/migrate"].first) + railties[railtie.railtie_name] = path + end + end + + on_skip = Proc.new do |name, migration| + puts "NOTE: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists." + end + + on_copy = Proc.new do |name, migration| + puts "Copied migration #{migration.basename} from #{name}" + end + + ActiveRecord::Migration.copy(ActiveRecord::Tasks::DatabaseTasks.migrations_paths.first, railties, + on_skip: on_skip, on_copy: on_copy) + end + end +end diff --git a/activerecord/lib/active_record/railties/jdbcmysql_error.rb b/activerecord/lib/active_record/railties/jdbcmysql_error.rb new file mode 100644 index 0000000000..72c75ddd52 --- /dev/null +++ b/activerecord/lib/active_record/railties/jdbcmysql_error.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +#FIXME Remove if ArJdbcMysql will give. +module ArJdbcMySQL #:nodoc: + class Error < StandardError #:nodoc: + attr_accessor :error_number, :sql_state + + def initialize(msg) + super + @error_number = nil + @sql_state = nil + end + + # Mysql gem compatibility + alias_method :errno, :error_number + alias_method :error, :message + end +end |