Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Emmanuel Oga
authored and
Emmanuel Oga
committed
Feb 23, 2010
1 parent
5bca743
commit ebef31c
Showing
7 changed files
with
153 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
require 'erb' | ||
|
||
module LoadDataInfile | ||
module MySql | ||
|
||
# Deletes all rows in table very fast, but without calling +destroy+ method | ||
# nor any hooks. | ||
def truncate_table | ||
connection.execute("TRUNCATE TABLE #{quoted_table_name}") | ||
end | ||
|
||
# Disables key updates for model table | ||
def disable_keys | ||
connection.execute("ALTER TABLE #{quoted_table_name} DISABLE KEYS") | ||
end | ||
|
||
# Enables key updates for model table | ||
def enable_keys | ||
connection.execute("ALTER TABLE #{quoted_table_name} ENABLE KEYS") | ||
end | ||
|
||
# Disables keys, yields block, enables keys. | ||
def with_keys_disabled | ||
disable_keys | ||
yield | ||
enable_keys | ||
end | ||
|
||
def load_data_infile(options = {}) | ||
c = Context.new | ||
|
||
if options[:low_priority] | ||
c.low_priority_or_concurrent = :LOW_PRIORITY | ||
elsif options[:concurrent] | ||
c.low_priority_or_concurrent = :CONCURRENT | ||
end | ||
|
||
c.local = :LOCAL if !options.member?(:local) || options[:local] | ||
|
||
c.file_name = options[:path] | ||
|
||
c.replace_or_ignore = options[:on_duplicates] if options[:on_duplicates] # REPLACE or IGNORE | ||
|
||
c.table_name = options[:table] ? "`#{ options[:table] }`" : quoted_table_name | ||
|
||
c.charset = "CHARACTER SET #{options[:charset]}" if options[:charset] | ||
|
||
if options[:terminated_by] || options[:enclosed_by] || options[:optionally_enclosed_by] || options[:escaped_by] | ||
c.fields_definitions = " FIELDS " # or COLUMNS | ||
c.fields_definitions << " TERMINATED BY '#{ options[:terminated_by] }' " if options[:terminated_by] | ||
c.fields_definitions << " ENCLOSED BY '#{ options[:enclosed_by] }' " if options[:enclosed_by] | ||
c.fields_definitions << " OPTIONALLY ENCLOSED BY '#{ options[:optionally_enclosed_by] }' " if options[:optionally_enclosed_by] | ||
c.fields_definitions << " ESCAPED BY '#{ options[:escaped_by] }' " if options[:escaped_by] | ||
end | ||
|
||
if options[:lines_terminated_by] || options[:lines_starting_by] | ||
c.lines_defitions = " LINES " | ||
c.lines_defitions << " STARTING BY '#{options[:lines_starting_by]}' " if options[:lines_starting_by] | ||
c.lines_defitions << " TERMINATED BY '#{options[:lines_terminated_by]}' " if options[:lines_terminated_by] | ||
end | ||
|
||
c.ignores = "IGNORE #{options[:ignore]} LINES" if options[:ignore] | ||
|
||
c.columns = " (#{options[:columns].join(", ")}) " if options[:columns] | ||
|
||
if options[:mappings] && options[:mappings].length > 0 | ||
s = options[:mapping].map{|column, mapping| "#{column} = #{mapping}" }.join(",") | ||
c.mappings = "SET #{s}" | ||
end | ||
|
||
connection.execute(ERB.new(LOAD_DATA_INFILE_SQL).result(c.binding).gsub(/^\s*\n/, "")) | ||
end | ||
|
||
class Context < OpenStruct | ||
public :binding | ||
end | ||
|
||
LOAD_DATA_INFILE_SQL = <<-SQL | ||
LOAD DATA <%= low_priority_or_concurrent %> <%= local %> INFILE '<%= file_name %>' | ||
<%= replace_or_ignore %> | ||
INTO TABLE <%= table_name %> | ||
<%= charset %> | ||
<%= fields_definitions %> | ||
<%= lines_defitions %> | ||
<%= ignores %> | ||
<%= columns %> | ||
<%= mappings %> ; | ||
SQL | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ActiveRecord::Base.extend LoadDataInfile::MySql |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
ActiveRecord::Base.establish_connection(:adapter => "mysql", :database => "load_data_infile_test") | ||
|
||
ActiveRecord::Base.logger = Logger.new(STDOUT) | ||
|
||
ActiveRecord::Schema.define do | ||
create_table "things", :force => true do |t| | ||
t.string :field_a, :field_b | ||
t.integer :field_c | ||
end | ||
end | ||
|
||
class Thing < ActiveRecord::Base | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
id,a,b,c | ||
71,Hello,Brother,42 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
61,live,from,2400 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,40 @@ | ||
require File.expand_path(File.dirname(__FILE__) + '/spec_helper') | ||
|
||
describe "LoadDataInfile" do | ||
it "fails" do | ||
fail "hey buddy, you should probably rename this file and start specing for real" | ||
describe LoadDataInfile do | ||
before :each do | ||
Thing.truncate_table | ||
end | ||
|
||
it "loads data from a csv file with headers into an ActiveRecord table" do | ||
Thing.with_keys_disabled do | ||
Thing.load_data_infile( | ||
:path => FIXTURE_WITH_HEADERS, | ||
:columns => %w|id field_a field_b field_c|, | ||
:terminated_by => ",", | ||
:ignore => 1 | ||
) | ||
end | ||
Thing.all.map(&:attributes).should == [{ | ||
"id" => 71, | ||
"field_a" => "Hello", | ||
"field_b" => "Brother", | ||
"field_c" => 42 | ||
}] | ||
end | ||
|
||
it "loads data from a csv file without headers into an ActiveRecord table" do | ||
Thing.with_keys_disabled do | ||
Thing.load_data_infile( | ||
:path => FIXTURE_WITHOUT_HEADERS, | ||
:terminated_by => ",", | ||
:columns => %w|id field_a field_b field_c| | ||
) | ||
end | ||
Thing.all.map(&:attributes).should == [{ | ||
"id" => 61, | ||
"field_a" => "live", | ||
"field_b" => "from", | ||
"field_c" => 2400 | ||
}] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,16 @@ | ||
$LOAD_PATH.unshift(File.dirname(__FILE__)) | ||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) | ||
SPEC_PATH = File.dirname(__FILE__) | ||
$LOAD_PATH.unshift(SPEC_PATH) | ||
$LOAD_PATH.unshift(File.join(SPEC_PATH, '..', 'lib')) | ||
require 'load_data_infile' | ||
require 'spec' | ||
require 'spec/autorun' | ||
require 'rubygems' | ||
require 'active_record' | ||
require 'active_record_helper' | ||
require File.join(SPEC_PATH, "..", "rails", "init.rb") | ||
|
||
FIXTURE_WITH_HEADERS = File.join(SPEC_PATH, "fixtures", "csv_with_headers.csv") | ||
FIXTURE_WITHOUT_HEADERS = File.join(SPEC_PATH, "fixtures", "csv_without_headers.csv") | ||
|
||
Spec::Runner.configure do |config| | ||
|
||
end |