diff options
Diffstat (limited to 'activerecord/lib/active_record/railties')
-rw-r--r-- | activerecord/lib/active_record/railties/databases.rake | 254 | ||||
-rw-r--r-- | activerecord/lib/active_record/railties/jdbcmysql_error.rb | 4 |
2 files changed, 55 insertions, 203 deletions
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 822b51e838..b8205a99a8 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -14,138 +14,27 @@ db_namespace = namespace :db do end namespace :create do - # desc 'Create all the local databases defined in config/database.yml' task :all => :load_config do - ActiveRecord::Base.configurations.each_value do |config| - # Skip entries that don't have a database key, such as the first entry here: - # - # defaults: &defaults - # adapter: mysql - # username: root - # password: - # host: localhost - # - # development: - # database: blog_development - # *defaults - next unless config['database'] - # Only connect to local databases - local_database?(config) { create_database(config) } - end + ActiveRecord::Tasks::DatabaseTasks.create_all end end desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)' task :create => :load_config do - configs_for_environment.each { |config| create_database(config) } - ActiveRecord::Base.establish_connection(configs_for_environment.first) - end - - def mysql_creation_options(config) - @charset = ENV['CHARSET'] || 'utf8' - @collation = ENV['COLLATION'] || 'utf8_unicode_ci' - {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)} - end - - def create_database(config) - begin - if config['adapter'] =~ /sqlite/ - if File.exist?(config['database']) - $stderr.puts "#{config['database']} already exists" - else - begin - # Create the SQLite database - ActiveRecord::Base.establish_connection(config) - ActiveRecord::Base.connection - rescue Exception => e - $stderr.puts e, *(e.backtrace) - $stderr.puts "Couldn't create database for #{config.inspect}" - end - end - return # Skip the else clause of begin/rescue - else - ActiveRecord::Base.establish_connection(config) - ActiveRecord::Base.connection - end - rescue - case config['adapter'] - when /mysql/ - if config['adapter'] =~ /jdbc/ - #FIXME After Jdbcmysql gives this class - require 'active_record/railties/jdbcmysql_error' - error_class = ArJdbcMySQL::Error - else - error_class = config['adapter'] =~ /mysql2/ ? Mysql2::Error : Mysql::Error - end - access_denied_error = 1045 - begin - ActiveRecord::Base.establish_connection(config.merge('database' => nil)) - ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config)) - ActiveRecord::Base.establish_connection(config) - rescue error_class => sqlerr - if sqlerr.errno == access_denied_error - print "#{sqlerr.error}. \nPlease provide the root password for your mysql installation\n>" - root_password = $stdin.gets.strip - grant_statement = "GRANT ALL PRIVILEGES ON #{config['database']}.* " \ - "TO '#{config['username']}'@'localhost' " \ - "IDENTIFIED BY '#{config['password']}' WITH GRANT OPTION;" - ActiveRecord::Base.establish_connection(config.merge( - 'database' => nil, 'username' => 'root', 'password' => root_password)) - ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config)) - ActiveRecord::Base.connection.execute grant_statement - ActiveRecord::Base.establish_connection(config) - else - $stderr.puts sqlerr.error - $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation}" - $stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['charset'] - end - end - when /postgresql/ - @encoding = config['encoding'] || ENV['CHARSET'] || 'utf8' - begin - ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) - ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding)) - ActiveRecord::Base.establish_connection(config) - rescue Exception => e - $stderr.puts e, *(e.backtrace) - $stderr.puts "Couldn't create database for #{config.inspect}" - end - end - else - $stderr.puts "#{config['database']} already exists" - end + ActiveRecord::Tasks::DatabaseTasks.create_current end namespace :drop do - # desc 'Drops all the local databases defined in config/database.yml' task :all => :load_config do - ActiveRecord::Base.configurations.each_value do |config| - # Skip entries that don't have a database key - next unless config['database'] - begin - # Only connect to local databases - local_database?(config) { drop_database(config) } - rescue Exception => e - $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}" - end - end + ActiveRecord::Tasks::DatabaseTasks.drop_all end end desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)' task :drop => :load_config do - configs_for_environment.each { |config| drop_database_and_rescue(config) } - end - - def local_database?(config, &block) - if config['host'].in?(['127.0.0.1', 'localhost']) || config['host'].blank? - yield - else - $stderr.puts "This task only modifies local databases. #{config['database']} is on a remote host." - end + ActiveRecord::Tasks::DatabaseTasks.drop_current end - desc "Migrate the database (options: VERSION=x, VERBOSE=false)." task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true @@ -162,6 +51,9 @@ db_namespace = namespace :db do else raise "unknown schema format #{ActiveRecord::Base.schema_format}" 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 @@ -204,11 +96,12 @@ db_namespace = namespace :db do next # means "return" for rake task end db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}") + db_list.map! { |version| "%.3d" % version } file_list = [] ActiveRecord::Migrator.migrations_paths.each do |path| Dir.foreach(path) do |file| - # only files matching "20091231235959_some_name.rb" pattern - if match_data = /^(\d{14})_(.+)\.rb$/.match(file) + # match "20091231235959_some_name.rb" and "001_some_name.rb" pattern + if match_data = /^(\d{3,})_(.+)\.rb$/.match(file) status = db_list.delete(match_data[1]) ? 'up' : 'down' file_list << [status, match_data[1], match_data[2].humanize] end @@ -243,31 +136,18 @@ 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 do + task :reset => [:environment, :load_config] do db_namespace["drop"].invoke db_namespace["setup"].invoke end # desc "Retrieves the charset for the current environment's database" - task :charset => :environment do - config = ActiveRecord::Base.configurations[Rails.env || 'development'] - case config['adapter'] - when /mysql/ - ActiveRecord::Base.establish_connection(config) - puts ActiveRecord::Base.connection.charset - when /postgresql/ - ActiveRecord::Base.establish_connection(config) - puts ActiveRecord::Base.connection.encoding - when /sqlite/ - ActiveRecord::Base.establish_connection(config) - puts ActiveRecord::Base.connection.encoding - else - $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' - end + 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 do + task :collation => [:environment, :load_config] do config = ActiveRecord::Base.configurations[Rails.env || 'development'] case config['adapter'] when /mysql/ @@ -279,12 +159,12 @@ db_namespace = namespace :db do end desc 'Retrieves the current schema version number' - task :version => :environment do + 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 do + task :abort_if_pending_migrations => [:environment, :load_config] do pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations if pending_migrations.any? @@ -307,20 +187,20 @@ db_namespace = namespace :db do 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." - task :load => :environment do + task :load => [:environment, :load_config] do require 'active_record/fixtures' ActiveRecord::Base.establish_connection(Rails.env) base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact - (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.{yml,csv}"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file) end 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 do + task :identify => [:environment, :load_config] do require 'active_record/fixtures' label, id = ENV['LABEL'], ENV['ID'] @@ -356,7 +236,7 @@ db_namespace = namespace :db do end desc 'Load a schema.rb file into the database' - task :load => :environment do + task :load => [:environment, :load_config] do file = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb" if File.exists?(file) load(file) @@ -368,28 +248,38 @@ db_namespace = namespace :db do task :load_if_ruby => 'db:create' do db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby end + + namespace :cache do + desc 'Create a db/schema_cache.dump file.' + task :dump => [:environment, :load_config] do + con = ActiveRecord::Base.connection + filename = File.join(Rails.application.config.paths["db"].first, "schema_cache.dump") + + con.schema_cache.clear! + con.tables.each { |table| con.schema_cache.add(table) } + open(filename, 'wb') { |f| f.write(Marshal.dump(con.schema_cache)) } + end + + desc 'Clear a db/schema_cache.dump file.' + task :clear => [:environment, :load_config] do + filename = File.join(Rails.application.config.paths["db"].first, "schema_cache.dump") + FileUtils.rm(filename) if File.exists?(filename) + end + end + end namespace :structure do desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql' - task :dump => :environment do + task :dump => [:environment, :load_config] do abcs = ActiveRecord::Base.configurations filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql") case abcs[Rails.env]['adapter'] - when /mysql/, 'oci', 'oracle' + when /mysql/, /postgresql/, /sqlite/ + ActiveRecord::Tasks::DatabaseTasks.structure_dump(abcs[Rails.env], filename) + when 'oci', 'oracle' ActiveRecord::Base.establish_connection(abcs[Rails.env]) File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump } - when /postgresql/ - set_psql_env(abcs[Rails.env]) - search_path = abcs[Rails.env]['schema_search_path'] - unless search_path.blank? - search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ") - end - `pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}` - raise 'Error dumping database' if $?.exitstatus == 1 - when /sqlite/ - dbfile = abcs[Rails.env]['database'] - `sqlite3 #{dbfile} .schema > #{filename}` when 'sqlserver' `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f #{filename} -A -U` when "firebird" @@ -403,6 +293,7 @@ db_namespace = namespace :db do if ActiveRecord::Base.connection.supports_migrations? File.open(filename, "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } end + db_namespace['structure:dump'].reenable end # desc "Recreate the databases from the structure.sql file" @@ -420,7 +311,7 @@ db_namespace = namespace :db do end when /postgresql/ set_psql_env(abcs[env]) - `psql -f "#{filename}" #{abcs[env]['database']} #{abcs[env]['template']}` + `psql -f "#{filename}" #{abcs[env]['database']}` when /sqlite/ dbfile = abcs[env]['database'] `sqlite3 #{dbfile} < "#{filename}"` @@ -454,7 +345,7 @@ db_namespace = namespace :db do db_namespace["test:load_schema"].invoke when :sql db_namespace["test:load_structure"].invoke - end + end end # desc "Recreate the test database from an existent structure.sql file" @@ -481,19 +372,11 @@ db_namespace = namespace :db do task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ] # desc "Empty the test database" - task :purge => :environment do + task :purge => [:environment, :load_config] do abcs = ActiveRecord::Base.configurations case abcs['test']['adapter'] - when /mysql/ - ActiveRecord::Base.establish_connection(:test) - ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], mysql_creation_options(abcs['test'])) - when /postgresql/ - ActiveRecord::Base.clear_active_connections! - drop_database(abcs['test']) - create_database(abcs['test']) - when /sqlite/ - dbfile = abcs['test']['database'] - File.delete(dbfile) if File.exist?(dbfile) + when /mysql/, /postgresql/, /sqlite/ + ActiveRecord::Tasks::DatabaseTasks.purge abcs['test'] when 'sqlserver' test = abcs.deep_dup['test'] test_database = test['database'] @@ -523,7 +406,7 @@ db_namespace = namespace :db do namespace :sessions do # desc "Creates a sessions migration for use with ActiveRecord::SessionStore" - task :create => :environment do + task :create => [:environment, :load_config] do raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations? Rails.application.load_generators require 'rails/generators/rails/session_migration/session_migration_generator' @@ -531,7 +414,7 @@ db_namespace = namespace :db do end # desc "Clear the sessions table" - task :clear => :environment do + task :clear => [:environment, :load_config] do ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}" end end @@ -542,7 +425,7 @@ namespace :railties 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 {|n| n.strip } - railties = ActiveSupport::OrderedHash.new + railties = {} Rails.application.railties.all do |railtie| next unless to_load == :all || to_load.include?(railtie.railtie_name) @@ -567,37 +450,6 @@ end task 'test:prepare' => 'db:test:prepare' -def drop_database(config) - case config['adapter'] - when /mysql/ - ActiveRecord::Base.establish_connection(config) - ActiveRecord::Base.connection.drop_database config['database'] - when /sqlite/ - require 'pathname' - path = Pathname.new(config['database']) - file = path.absolute? ? path.to_s : File.join(Rails.root, path) - - FileUtils.rm(file) - when /postgresql/ - ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) - ActiveRecord::Base.connection.drop_database config['database'] - end -end - -def drop_database_and_rescue(config) - begin - drop_database(config) - rescue Exception => e - $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}" - end -end - -def configs_for_environment - environments = [Rails.env] - environments << 'test' if Rails.env.development? - ActiveRecord::Base.configurations.values_at(*environments).compact.reject { |config| config['database'].blank? } -end - def session_table_name ActiveRecord::SessionStore::Session.table_name end @@ -612,7 +464,7 @@ def firebird_db_string(config) end def set_psql_env(config) - ENV['PGHOST'] = config['host'] if config['host'] + ENV['PGHOST'] = config['host'] if config['host'] ENV['PGPORT'] = config['port'].to_s if config['port'] ENV['PGPASSWORD'] = config['password'].to_s if config['password'] ENV['PGUSER'] = config['username'].to_s if config['username'] diff --git a/activerecord/lib/active_record/railties/jdbcmysql_error.rb b/activerecord/lib/active_record/railties/jdbcmysql_error.rb index 6b9af2a0cb..6a38211bff 100644 --- a/activerecord/lib/active_record/railties/jdbcmysql_error.rb +++ b/activerecord/lib/active_record/railties/jdbcmysql_error.rb @@ -1,6 +1,6 @@ #FIXME Remove if ArJdbcMysql will give. -module ArJdbcMySQL - class Error < StandardError +module ArJdbcMySQL #:nodoc: + class Error < StandardError #:nodoc: attr_accessor :error_number, :sql_state def initialize msg |