How can execute multiple statements in one query with Rails? - ruby-on-rails

I am using Ruby on Rails with ActiveRecord and PostgreSQL.
How can i execute multiple sql queries?
I need it for running a custom migration script, eg:
Foo.connection.execute <<-SQL.split(';').map(&:strip).join
delete from metadata where record_type = 'Foo';
TRUNCATE table1 RESTART IDENTITY;
TRUNCATE table2 RESTART IDENTITY;
delete from schema_migrations where version > '20120806120823';
SQL
I am not accepting data from a user, so I'm not worried about sql-injection.
Something like CLIENT_MULTI_STATEMENTS in MySQL maybe ?
From the MySQL/PHP docs:
CLIENT_MULTI_STATEMENTS: Tell the server that the client may send
multiple statements in a single string (separated by “;”). If this
flag is not set, multiple-statement execution is disabled. See the
note following this table for more information about this flag.

It should work out of the box with PostgreSQL, checked with pg gem and rails 3.2:
class Multitest < ActiveRecord::Migration
def up
execute <<-SQL
create table x(id serial primary key);
create table y(id serial primary key, i integer);
SQL
end
def down
end
end
On a side note, manipulating schema_migrations directly looks strange.

For mysql
queries = File.read("/where/is/myqueries.sql")
# or
queries = <<-SQL
TRUNCATE table1 RESTART IDENTITY;
TRUNCATE table2 RESTART IDENTITY;
delete from schema_migrations where version > '20120806120823';
SQL
queries.split(';').map(&:strip).each do |query|
execute(query)
end
You may want see this question too:
Invoking a large set of SQL from a Rails 4 application

Yes, you need CLIENT_MULTI_STATEMENTS:
In database.yml:
development:
adapter: mysql2
database: project_development
flags:
- MULTI_STATEMENTS
Then in your code:
connection.execute(multistatement_query)
# Hack for mysql2 adapter to be able query again after executing multistatement_query
connection.raw_connection.store_result while connection.raw_connection.next_result
See https://stackoverflow.com/a/11246837/338859 for details

Related

Rails 7 - selecting database with multi-database when calling ActiveRecord::Base.connection.execute

The new multi-database feature is great, however it is unclear to me how you can still execute raw mysql calling ActiveRecord::Base.connection.execute and have it select a specific database in the database.yml file.
this
ActiveRecord::Base.connected_to(role: :reading) do
end
Doesnt accept a database
ActiveRecord::Base.connection.execute always seems to USE the first database in the config file.
thanks
Tried: ActiveRecord::Base.connected_to do ...
doesn't expect a database

truncate and restart identity of a set of tables

My application uses Postgresql.
I would need to remove all rows from a set of tables (table1 to table4) and restart the id with one command from a rb file.
In the Postgresql documentation I found that TRUNCATE with RESTART IDENTITY would do the job as follows:
TRUNCATE table1, table2, table3, table4 RESTART IDENTITY;
According to How to restart id counting on a table in PostgreSQL after deleting some previous data? at Stackoverflow, I can use the following command:
ActiveRecord::Base.connection.execute("TRUNCATE TABLE your_table_name RESTART IDENTITY")
So putting together the two documentations, would it be correct to use the following command:
ActiveRecord::Base.connection.execute("TRUNCATE table1, table2, table3, table4 RESTART IDENTITY")
considering that in the API dock documentation the connection method is reported as deprecated or moved?.
The recommended way nowadays is to use an ActiveRecord::Base.connection_pool.with_connection block.
ActiveRecord::Base.connection_pool.with_connection do |conn|
conn.execute("TRUNCATE table1, table2, table3, table4 RESTART IDENTITY")
end

Rails seed sql file that contains "COPY" statments

I am trying to seed sql file that looks like that:
COPY courses (id, name, "courseID", school_id, created_at, updated_at) FROM stdin;
1 Fundamentals of Programming CSCI150 1 2016-04-27 14:04:07.460825 2016-04-27 14:04:07.460825
I try this code:
connection = ActiveRecord::Base.connection
# migrate all courses
sql = File.read('db/nurate.sql')
statements = sql.split(/;$/)
#statements.pop
ActiveRecord::Base.transaction do
statements.each do |statement|
connection.execute(statement)
end
end
Previously I used this code for INSERT etc statements. But here I have error as follows:
PG::UnableToSend: another command is already in progress
So Believe this has something to do with the fact that my sql dump file contains "COPY" statements. What can I do with this? I don't want to change the whole file to use INSERT statements. Or is it the only solution?
I've got same issue and it costed me couple of days.
Firstly I guess that you've used the sql from dump export feature of postgres. So because of that when ever you call
connection.execute(statement)
It will cause error because it's never been a legit call for query in postgresql with provide input data. I've done some check over internet. It seem this thread provide some helpful information.
Okei so in order to keep using buck copy combine with the sql file export by pg_dump, I have come up with quite dirty code (IMO)
unless Rails.env.production?
connection = ActiveRecord::Base.connection
connection.tables.each do |table|
connection.execute("TRUNCATE #{table}") unless table == "schema_migrations"
end
# Create a sql file with purely COPY data and ignoring schema_migrations data
cmd = %x[awk -v RS='' '/COPY/{if (!match($0,/schema/)) print $0}' #{Rails.root}/db/dump.sql> #{Rails.root}/db/temp.sql]
puts cmd
# Restoring sql data to postgres data. Putting password to env variable is never been smart
cmd = %x[PGPASSWORD=#{ActiveRecord::Base.connection_config[:password]} psql --host #{ActiveRecord::Base.connection_config[:host]} -U #{ActiveRecord::Base.connection_config[:username]} --dbname #{ActiveRecord::Base.connection_config[:database]} -1 -f #{Rails.root}/db/temp.sql]
puts cmd
# remove the deed
cmd = %x[rm #{Rails.root}/db/temp.sql]
puts cmd
end

How to delete everything from all tables without dropping the database [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Truncating all tables in a postgres database
How can I delete all data from all tables without dropping the database?
You can use raw connection object to execute SQL statements:
connection = ActiveRecord::Base.connection
connection.tables.each{|t| connection.execute "TRUNCATE #{t}"}
Use the DatabaseCleaner gem.
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
If you absolutely must have this within a rake task, just wrap it up in one yourself.
For ad-hoc use
Run this statement in the database (Careful! Nukes all your data!):
DO
$func$
BEGIN
EXECUTE (
SELECT 'TRUNCATE TABLE '
|| string_agg(quote_ident(t.tablename), ', ')
|| ' CASCADE'
FROM pg_tables t
WHERE t.schemaname = 'public' -- assuming default schema
);
END
$func$;
The DO command was introduced with PostgreSQL 9.0. You would create a plpgsql function and execute it for older versions.
Compare to the closely related question: Truncating all tables in a Postgres database
For repeated use
It might be simpler (and faster!) to create a "template" database (let's name it my_template) with your vanilla structure and all empty tables. Then go through a DROP / CREATE DATABASE cycle:
DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;
This is extremely fast, because PostgreSQL copies the whole structure on the file level. No concurrency issues or other overhead slowing you down.
You can do a backup of the database with pg_dump and restore only the schema of the database with pg_resotre --schema-only , it deletes all data in all tables.
Exemple:
To backup.
pg_dump --format=c --compress=0 -h localhost mydatabasename > mydump.dmp
To restore only schema information without data.
pg_restore -c --schema-only mydump.dmp | psql -h localhost mydatabasename

Execute sql script inside seed.rb in rails3

I want to execute this sql script inside my seed.rb
LOAD DATA LOCAL INFILE '/home/list-38.csv'
INTO TABLE list
FIELDS TERMINATED BY ':'
LINES TERMINATED BY '\n'
(email,name,password);
I checked this link but unable to figure out the solution.So that once we run rake db:seed
How to seed mysql database by running sql scripts in Ruby Rails platform? it dumps my data into the table.
any queries ..do reply
Thanx
Try this in db/seeds.rb to execute raw SQL with rake db:seed
connection = ActiveRecord::Base.connection()
connection.execute("*_YOUR_SQL_HERE_*")
For multiple sql statements, I ended up iterating over each statement separately:
# db/seeds.rb
connection = ActiveRecord::Base.connection()
sql = <<-EOL
INSERT INTO users values ('Admin');
INSERT INTO categories values ('Supervisors');
EOL
sql.split(';').each do |s|
connection.execute(s.strip) unless s.strip.empty?
end
A variation on Fredrik Boström's answer that can load from a file:
# db/seeds.rb
def execute_sql_file(path, connection = ActiveRecord::Base.connection)
require 'active_support/core_ext/object/blank.rb'
IO.read(path).split(';').reject(&:blank?).each do |statement|
connection.execute(statement)
end
end
execute_sql_file('my.sql')
Super late to this answer but hopefully it helps someone in my position. Instead of iterating over each sql statement you can use .execute_batch using a direct connection to your database with the database version you are using. Below is an example with SQLite3.
#db/seeds.rb
DB = {:conn => SQLite3::Database.new("./db/development.sqlite")}
sql = <<-EOL
INSERT INTO users values ('Admin');
INSERT INTO categories values ('Supervisors');
EOL
DB[:conn].execute_batch(sql)

Resources