Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/trunk/railties/lib/tasks/databases.rake

Revision 9183, 16.0 kB (checked in by bitsweat, 1 year ago)

PostgreSQL: use create_ and drop_database for rake tasks. Closes #9045 [ez, nicksieger]

Line 
1 namespace :db do
2   namespace :create do
3     desc 'Create all the local databases defined in config/database.yml'
4     task :all => :environment do
5       ActiveRecord::Base.configurations.each_value do |config|
6         # Skip entries that don't have a database key, such as the first entry here:
7         #
8         #  defaults: &defaults
9         #    adapter: mysql
10         #    username: root
11         #    password:
12         #    host: localhost
13         #
14         #  development:
15         #    database: blog_development
16         #    <<: *defaults
17         next unless config['database']
18         # Only connect to local databases
19         local_database?(config) { create_database(config) }
20       end
21     end
22   end
23
24   desc 'Create the database defined in config/database.yml for the current RAILS_ENV'
25   task :create => :environment do
26     create_database(ActiveRecord::Base.configurations[RAILS_ENV])
27   end
28
29   def create_database(config)
30     begin
31       ActiveRecord::Base.establish_connection(config)
32       ActiveRecord::Base.connection
33     rescue
34       case config['adapter']
35       when 'mysql'
36         @charset   = ENV['CHARSET']   || 'utf8'
37         @collation = ENV['COLLATION'] || 'utf8_general_ci'
38         begin
39           ActiveRecord::Base.establish_connection(config.merge('database' => nil))
40           ActiveRecord::Base.connection.create_database(config['database'], :charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation))
41           ActiveRecord::Base.establish_connection(config)
42         rescue
43           $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation} (if you set the charset manually, make sure you have a matching collation)"
44         end
45       when 'postgresql'
46         @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
47         begin
48           ActiveRecord::Base.establish_connection(config.merge('database' => nil))
49           ActiveRecord::Base.connection.create_database(config['database'], :encoding => @encoding)
50           ActiveRecord::Base.establish_connection(config)
51         rescue
52           $stderr.puts $!, *($!.backtrace)
53           $stderr.puts "Couldn't create database for #{config.inspect}"
54         end
55       when 'sqlite'
56         `sqlite "#{config['database']}"`
57       when 'sqlite3'
58         `sqlite3 "#{config['database']}"`
59       end
60     else
61       $stderr.puts "#{config['database']} already exists"
62     end
63   end
64
65   namespace :drop do
66     desc 'Drops all the local databases defined in config/database.yml'
67     task :all => :environment do
68       ActiveRecord::Base.configurations.each_value do |config|
69         # Skip entries that don't have a database key
70         next unless config['database']
71         # Only connect to local databases
72         local_database?(config) { drop_database(config) }
73       end
74     end
75   end
76
77   desc 'Drops the database for the current RAILS_ENV'
78   task :drop => :environment do
79     config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
80     begin
81       drop_database(config)
82     rescue Exception => e
83       puts "Couldn't drop #{config['database']} : #{e.inspect}"
84     end
85   end
86
87   def local_database?(config, &block)
88     if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
89       yield
90     else
91       puts "This task only modifies local databases. #{config['database']} is on a remote host."
92     end
93   end
94
95
96   desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
97   task :migrate => :environment do
98     ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
99     ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
100     Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
101   end
102
103   namespace :migrate do
104     desc  'Rollbacks the database one migration and re migrate up. If you want to rollback more than one step, define STEP=x'
105     task :redo => [ 'db:rollback', 'db:migrate' ]
106
107     desc 'Resets your database using your migrations for the current environment'
108     task :reset => ["db:drop", "db:create", "db:migrate"]
109
110     desc 'Runs the "up" for a given migration VERSION.'
111     task :up => :environment do
112       version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
113       raise "VERSION is required" unless version
114       ActiveRecord::Migrator.run(:up, "db/migrate/", version)
115       Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
116     end
117
118     desc 'Runs the "down" for a given migration VERSION.'
119     task :down => :environment do
120       version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
121       raise "VERSION is required" unless version
122       ActiveRecord::Migrator.run(:down, "db/migrate/", version)
123       Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
124     end
125   end
126
127   desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n'
128   task :rollback => :environment do
129     step = ENV['STEP'] ? ENV['STEP'].to_i : 1
130     ActiveRecord::Migrator.rollback('db/migrate/', step)
131     Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
132   end
133
134   desc 'Drops and recreates the database from db/schema.rb for the current environment.'
135   task :reset => ['db:drop', 'db:create', 'db:schema:load']
136
137   desc "Retrieves the charset for the current environment's database"
138   task :charset => :environment do
139     config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
140     case config['adapter']
141     when 'mysql'
142       ActiveRecord::Base.establish_connection(config)
143       puts ActiveRecord::Base.connection.charset
144     else
145       puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
146     end
147   end
148
149   desc "Retrieves the collation for the current environment's database"
150   task :collation => :environment do
151     config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
152     case config['adapter']
153     when 'mysql'
154       ActiveRecord::Base.establish_connection(config)
155       puts ActiveRecord::Base.connection.collation
156     else
157       puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
158     end
159   end
160
161   desc "Retrieves the current schema version number"
162   task :version => :environment do
163     puts "Current version: #{ActiveRecord::Migrator.current_version}"
164   end
165
166   desc "Raises an error if there are pending migrations"
167   task :abort_if_pending_migrations => :environment do
168     if defined? ActiveRecord
169       pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations
170
171       if pending_migrations.any?
172         puts "You have #{pending_migrations.size} pending migrations:"
173         pending_migrations.each do |pending_migration|
174           puts '  %4d %s' % [pending_migration.version, pending_migration.name]
175         end
176         abort "Run `rake db:migrate` to update your database then try again."
177       end
178     end
179   end
180
181   namespace :fixtures do
182     desc "Load fixtures into the current environment's database.  Load specific fixtures using FIXTURES=x,y"
183     task :load => :environment do
184       require 'active_record/fixtures'
185       ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
186       (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'test', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
187         Fixtures.create_fixtures('test/fixtures', File.basename(fixture_file, '.*'))
188       end
189     end
190
191     desc "Search for a fixture given a LABEL or ID."
192     task :identify => :environment do
193       require "active_record/fixtures"
194
195       label, id = ENV["LABEL"], ENV["ID"]
196       raise "LABEL or ID required" if label.blank? && id.blank?
197
198       puts %Q(The fixture ID for "#{label}" is #{Fixtures.identify(label)}.) if label
199
200       Dir["#{RAILS_ROOT}/test/fixtures/**/*.yml"].each do |file|
201         if data = YAML::load(ERB.new(IO.read(file)).result)
202           data.keys.each do |key|
203             key_id = Fixtures.identify(key)
204
205             if key == label || key_id == id.to_i
206               puts "#{file}: #{key} (#{key_id})"
207             end
208           end
209         end
210       end
211     end
212   end
213
214   namespace :schema do
215     desc "Create a db/schema.rb file that can be portably used against any DB supported by AR"
216     task :dump => :environment do
217       require 'active_record/schema_dumper'
218       File.open(ENV['SCHEMA'] || "db/schema.rb", "w") do |file|
219         ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
220       end
221     end
222
223     desc "Load a schema.rb file into the database"
224     task :load => :environment do
225       file = ENV['SCHEMA'] || "db/schema.rb"
226       load(file)
227     end
228   end
229
230   namespace :structure do
231     desc "Dump the database structure to a SQL file"
232     task :dump => :environment do
233       abcs = ActiveRecord::Base.configurations
234       case abcs[RAILS_ENV]["adapter"]
235       when "mysql", "oci", "oracle"
236         ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
237         File.open("db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
238       when "postgresql"
239         ENV['PGHOST']     = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"]
240         ENV['PGPORT']     = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"]
241         ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"]
242         search_path = abcs[RAILS_ENV]["schema_search_path"]
243         search_path = "--schema=#{search_path}" if search_path
244         `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{search_path} #{abcs[RAILS_ENV]["database"]}`
245         raise "Error dumping database" if $?.exitstatus == 1
246       when "sqlite", "sqlite3"
247         dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"]
248         `#{abcs[RAILS_ENV]["adapter"]} #{dbfile} .schema > db/#{RAILS_ENV}_structure.sql`
249       when "sqlserver"
250         `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r`
251         `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r`
252       when "firebird"
253         set_firebird_env(abcs[RAILS_ENV])
254         db_string = firebird_db_string(abcs[RAILS_ENV])
255         sh "isql -a #{db_string} > db/#{RAILS_ENV}_structure.sql"
256       else
257         raise "Task not supported by '#{abcs["test"]["adapter"]}'"
258       end
259
260       if ActiveRecord::Base.connection.supports_migrations?
261         File.open("db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
262       end
263     end
264   end
265
266   namespace :test do
267     desc "Recreate the test database from the current environment's database schema"
268     task :clone => %w(db:schema:dump db:test:purge) do
269       ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
270       ActiveRecord::Schema.verbose = false
271       Rake::Task["db:schema:load"].invoke
272     end
273
274
275     desc "Recreate the test databases from the development structure"
276     task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
277       abcs = ActiveRecord::Base.configurations
278       case abcs["test"]["adapter"]
279       when "mysql"
280         ActiveRecord::Base.establish_connection(:test)
281         ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
282         IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table|
283           ActiveRecord::Base.connection.execute(table)
284         end
285       when "postgresql"
286         ENV['PGHOST']     = abcs["test"]["host"] if abcs["test"]["host"]
287         ENV['PGPORT']     = abcs["test"]["port"].to_s if abcs["test"]["port"]
288         ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"]
289         `psql -U "#{abcs["test"]["username"]}" -f db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}`
290       when "sqlite", "sqlite3"
291         dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
292         `#{abcs["test"]["adapter"]} #{dbfile} < db/#{RAILS_ENV}_structure.sql`
293       when "sqlserver"
294         `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`
295       when "oci", "oracle"
296         ActiveRecord::Base.establish_connection(:test)
297         IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl|
298           ActiveRecord::Base.connection.execute(ddl)
299         end
300       when "firebird"
301         set_firebird_env(abcs["test"])
302         db_string = firebird_db_string(abcs["test"])
303         sh "isql -i db/#{RAILS_ENV}_structure.sql #{db_string}"
304       else
305         raise "Task not supported by '#{abcs["test"]["adapter"]}'"
306       end
307     end
308
309     desc "Empty the test database"
310     task :purge => :environment do
311       abcs = ActiveRecord::Base.configurations
312       case abcs["test"]["adapter"]
313       when "mysql"
314         ActiveRecord::Base.establish_connection(:test)
315         ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"])
316       when "postgresql"
317         ENV['PGHOST']     = abcs["test"]["host"] if abcs["test"]["host"]
318         ENV['PGPORT']     = abcs["test"]["port"].to_s if abcs["test"]["port"]
319         ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"]
320         enc_option = "-E #{abcs["test"]["encoding"]}" if abcs["test"]["encoding"]
321
322         ActiveRecord::Base.clear_active_connections!
323         `dropdb -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}`
324         `createdb #{enc_option} -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}`
325       when "sqlite","sqlite3"
326         dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
327         File.delete(dbfile) if File.exist?(dbfile)
328       when "sqlserver"
329         dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-')
330         `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}`
331         `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`
332       when "oci", "oracle"
333         ActiveRecord::Base.establish_connection(:test)
334         ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
335           ActiveRecord::Base.connection.execute(ddl)
336         end
337       when "firebird"
338         ActiveRecord::Base.establish_connection(:test)
339         ActiveRecord::Base.connection.recreate_database!
340       else
341         raise "Task not supported by '#{abcs["test"]["adapter"]}'"
342       end
343     end
344
345     desc 'Prepare the test database and load the schema'
346     task :prepare => %w(environment db:abort_if_pending_migrations) do
347       if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
348         Rake::Task[{ :sql  => "db:test:clone_structure", :ruby => "db:test:clone" }[ActiveRecord::Base.schema_format]].invoke
349       end
350     end
351   end
352
353   namespace :sessions do
354     desc "Creates a sessions migration for use with CGI::Session::ActiveRecordStore"
355     task :create => :environment do
356       raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
357       require 'rails_generator'
358       require 'rails_generator/scripts/generate'
359       Rails::Generator::Scripts::Generate.new.run(["session_migration", ENV["MIGRATION"] || "CreateSessions"])
360     end
361
362     desc "Clear the sessions table"
363     task :clear => :environment do
364       ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}"
365     end
366   end
367 end
368
369 def drop_database(config)
370   case config['adapter']
371   when 'mysql'
372     ActiveRecord::Base.connection.drop_database config['database']
373   when /^sqlite/
374     FileUtils.rm(File.join(RAILS_ROOT, config['database']))
375   when 'postgresql'
376     ActiveRecord::Base.establish_connection(config.merge('database' => nil))
377     ActiveRecord::Base.connection.drop_database config['database']
378   end
379 end
380
381 def session_table_name
382   ActiveRecord::Base.pluralize_table_names ? :sessions : :session
383 end
384
385 def set_firebird_env(config)
386   ENV["ISC_USER"]     = config["username"].to_s if config["username"]
387   ENV["ISC_PASSWORD"] = config["password"].to_s if config["password"]
388 end
389
390 def firebird_db_string(config)
391   FireRuby::Database.db_string_for(config.symbolize_keys)
392 end
Note: See TracBrowser for help on using the browser.