How to write SQL in a migration in Rails
For your up migration:
execute "CREATE TABLE cars_users2 AS SELECT DISTINCT * FROM cars_users;"
drop_table :car_users
rename_table :car_users2, :car_users
and for down:
raise ActiveRecord::IrreversibleMigration
Full migration:
class TheMigration < ActiveRecord::Migration
def up
execute "CREATE TABLE cars_users2 AS SELECT DISTINCT * from cars_users;"
drop_table :car_users
rename_table :car_users2, :car_users
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
Is it possible to output the SQL change scripts that 'rake db:migrate' produces?
You can create a Rake task in lib/tasks/
:
namespace :db do
desc 'Make migration with output'
task(:migrate_with_sql => :environment) do
ActiveRecord::Base.logger = Logger.new(STDOUT)
Rake::Task['db:migrate'].invoke
end
end
Then call rake db:migrate_with_sql
to log the migration.
Show SQL generated by pending migrations in rails without updating the database
This can be done by monkey-patching the database adapter. This example works for MySQL.
Create a rake task for "fake db:migrate":
desc "Prints all SQL to be executed during pending migrations"
task :fake_db_migrate => :environment do
module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
alias_method :real_execute, :execute
def execute(sql, name = nil)
if sql =~ /^SHOW/ || sql =~ /^SELECT.*FROM.*schema_migrations/ || sql =~ /^SELECT.*information_schema/m
real_execute(sql, name)
else
puts sql
end
end
end
end
end
Rake::Task["db:migrate"].invoke
end
The rake task monkey-patches the execute
method in the connection adapter so that SQL is printed instead of being executed, before actually running the migrations. However, we still have to execute some of the internal SQLs that are used by the db:migrate
task to get the database schema and to find out which migrations are pending. That's what the real_execute
call does.
Test
Suppose now that and we have a pending migration in db/migrate/20160211212415_create_some_table.rb
:
class CreateSomeTable < ActiveRecord::Migration
def change
create_table :some_table do |t|
t.string :string_column, null: false, default: 'ok'
t.timestamps
end
end
end
$ rake db:migrate:status
...
down 20160211212415 Create some table
Now, let's run our fake migrations task:
$ rake fake_db_migrate
== 20160211212415 CreateSomeTable: migrating ==================================
-- create_table(:some_table)
CREATE TABLE `some_table` (`id` int(11) auto_increment PRIMARY KEY, `string_column` varchar(255) DEFAULT 'ok' NOT NULL, `created_at` datetime, `updated_at` datetime) ENGINE=InnoDB
-> 0.0009s
== 20160211212415 CreateSomeTable: migrated (0.0010s) =========================
BEGIN
INSERT INTO `schema_migrations` (`version`) VALUES ('20160211212415')
COMMIT
The migrations status has not been changed, i.e. the migration is still pending:
$ rake db:migrate:status
...
down 20160211212415 Create some table
Tested on rails 4.2.3 with the mysql2
gem.
Executing sql in rails migration opens to sql injection attacks?
Your example is not likely to be at risk of sql injection. Regardless, it is good practice in Rails to sanitize raw sql fragements: http://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html
Example:
class MovePlayerToJoinTable < ActiveRecord::Migration
def change
games = Game.all
games.each do |g|
g.players.each do |p|
query = sanitize_sql_array(['insert into games_players (player_id, game_id) values (?, ?)', p.id, g.id])
connection.execute(query)
end
end
end
end
Creating mysql function from rails migration
Rails cannot execute DELIMITER because it is a MYSQL command.
ActiveRecord sets up a different end-of-statement flag (not a semi-colon), so you can just write the straight sql,
Just remove DELIMITTER and $$
How can you get a sql script of changes from Rails migrations (for MySQL)?
I actually ended up creating a rake task which monkey-patched Activerecord's sql execution method to also output all sql to a log file (log/database.log
). So, the task could be run right before db:migrate
like so: rake db:log db:migrate
. After that, you can extract the statements which are relevant and put them into a db/sql_migrations/<migration name>.sql
file and have your database admins run that when they're ready.
Thankfully I've changed jobs and don't have to deal with that mess of a process anymore though. :)
Related Topics
Sql Query Not Between Two Dates
Postgres How to Implement Calculated Column with Clause
How to Split a String into Variables in Sql
Sql Server Auto Increment a Column Without Primary Key
Convert from Uniqueidentifier to Bigint and Back
How to Add a Not Null Constraint on Column Containing Null Values
How to Trim All Data in a Select * from Statement
How to Get Id of Newly Inserted Record Using Excel Vba
Split Multiple Columns into Multiple Rows
Distinct() Function (Not Select Qualifier) in Postgres
Difference Between 'Load Data Inpath ' and 'Location' in Hive
Sql: Insert a Linebreak in Varchar String
How to Use Oracle Outer Join with a Filter Where Clause
Sql Server Creating a Temp Table for This Query
Database View Does Not Reflect The Data in The Underying Table
Passing C# Datatable as a Parameter to Stored Procedure in Ms SQL Server 2008