adapter_extensions 0.9.5 → 1.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +14 -0
- data/CHANGELOG +5 -0
- data/HOW_TO_RELEASE +4 -4
- data/LICENSE +1 -1
- data/README.md +121 -14
- data/Rakefile +38 -53
- data/adapter_extensions.gemspec +4 -2
- data/lib/adapter_extensions/active_record/adapters/abstract_adapter.rb +9 -0
- data/lib/adapter_extensions/active_record/adapters/mysql2_adapter.rb +6 -0
- data/lib/adapter_extensions/active_record/adapters/mysql_adapter.rb +6 -0
- data/lib/adapter_extensions/active_record/adapters/postgresql_adapter.rb +6 -0
- data/lib/adapter_extensions/active_record/adapters/sqlserver_adapter.rb +6 -0
- data/lib/adapter_extensions/adapters/abstract_adapter.rb +44 -0
- data/lib/adapter_extensions/adapters/mysql_adapter.rb +73 -0
- data/lib/adapter_extensions/adapters/postgresql_adapter.rb +47 -0
- data/lib/adapter_extensions/adapters/sqlserver_adapter.rb +63 -0
- data/lib/adapter_extensions/base.rb +19 -0
- data/lib/adapter_extensions/version.rb +1 -1
- data/lib/adapter_extensions.rb +17 -8
- data/test/abstract_adapter_test.rb +10 -4
- data/test/config/database.yml +15 -0
- data/test/{connection/mysql/setup.sql → config/databases/mysql_setup.sql} +0 -0
- data/test/{connection/postgresql/setup.sql → config/databases/postgresql_setup.sql} +0 -0
- data/test/config/gemfiles/.gitignore +1 -0
- data/test/config/gemfiles/Gemfile.rails-3.0.x +3 -0
- data/test/config/gemfiles/Gemfile.rails-3.1.x +3 -0
- data/test/config/gemfiles/Gemfile.rails-3.2.x +3 -0
- data/test/config/gemfiles/common.rb +22 -0
- data/test/integration/adapter_test.rb +23 -7
- data/test/test_helper.rb +3 -4
- data/test/unit/sqlserver_test_ignored.rb +89 -0
- metadata +86 -88
- data/lib/adapter_extensions/connection_adapters/abstract_adapter.rb +0 -50
- data/lib/adapter_extensions/connection_adapters/mysql_adapter.rb +0 -90
- data/lib/adapter_extensions/connection_adapters/postgresql_adapter.rb +0 -52
- data/lib/adapter_extensions/connection_adapters/sqlserver_adapter.rb +0 -44
- data/test/connection/mysql/connection.rb +0 -27
- data/test/connection/postgresql/connection.rb +0 -27
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
gemfile:
|
3
|
+
- test/config/gemfiles/Gemfile.rails-3.2.x
|
4
|
+
- test/config/gemfiles/Gemfile.rails-3.1.x
|
5
|
+
- test/config/gemfiles/Gemfile.rails-3.0.x
|
6
|
+
rvm:
|
7
|
+
- 1.9.3
|
8
|
+
- 1.8.7
|
9
|
+
env:
|
10
|
+
- DB=mysql2
|
11
|
+
- DB=mysql
|
12
|
+
- DB=postgresql
|
13
|
+
before_script:
|
14
|
+
- rake ci:create_db
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
1.0.0.rc1 - March 3, 2012
|
2
|
+
* complete rewrite: Rails 3+ required
|
3
|
+
* "mysql2" adapter support
|
4
|
+
* work in progress SQLServer bulk import, requiring freebcp
|
5
|
+
|
1
6
|
0.9.5 - November 8, 2011
|
2
7
|
* Add in REPLACE to LOAD DATA INFILE command (jayzes/kookster)
|
3
8
|
* Remove FasterCSV from dependencies (not used apparently)
|
data/HOW_TO_RELEASE
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
* update lib/adapter_extensions/version
|
2
2
|
* push your changes
|
3
|
-
*
|
3
|
+
* build the gem:
|
4
4
|
|
5
|
-
|
5
|
+
gem build adapter_extensions.gemspec
|
6
6
|
|
7
|
-
*
|
7
|
+
* create tag and push to rubygem:
|
8
8
|
|
9
|
-
|
9
|
+
rake release
|
10
10
|
|
11
11
|
* you can list changes using github:
|
12
12
|
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2011 Anthony Eden
|
1
|
+
Copyright (c) 2011-2012 Anthony Eden, Thibaut Barrère
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
4
4
|
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
data/README.md
CHANGED
@@ -1,23 +1,130 @@
|
|
1
|
-
|
1
|
+
AdapterExtensions add extra abilities to Rails ActiveRecord adapters, including:
|
2
2
|
|
3
|
-
|
3
|
+
* bulk load
|
4
|
+
* truncate table
|
5
|
+
* copy table
|
6
|
+
* add select into table
|
7
|
+
|
8
|
+
### Compatibility matrix
|
4
9
|
|
5
|
-
|
10
|
+
See Travis for up-to-date info: [](http://travis-ci.org/activewarehouse/adapter_extensions)
|
6
11
|
|
7
|
-
|
12
|
+
<table>
|
13
|
+
<tr>
|
14
|
+
<th></th>
|
15
|
+
<th>v1.0.0.rc1</th>
|
16
|
+
<th>v0.9.5</th>
|
17
|
+
</tr>
|
18
|
+
<tr>
|
19
|
+
<th>ActiveRecord adapters</th>
|
20
|
+
<td></td>
|
21
|
+
<td></td>
|
22
|
+
<tr>
|
23
|
+
<td>mysql</td>
|
24
|
+
<td>OK</td>
|
25
|
+
<td>OK</td>
|
26
|
+
</tr>
|
27
|
+
<tr>
|
28
|
+
<td>mysql2</td>
|
29
|
+
<td>OK</td>
|
30
|
+
<td>Unsupported</td>
|
31
|
+
</tr>
|
32
|
+
<tr>
|
33
|
+
<td>postgresql</td>
|
34
|
+
<td>OK</td>
|
35
|
+
<td>OK</td>
|
36
|
+
</tr>
|
37
|
+
<tr>
|
38
|
+
<td>sqlserver</td>
|
39
|
+
<td>Work in progress</td>
|
40
|
+
<td>Broken</td>
|
41
|
+
</tr>
|
42
|
+
<tr>
|
43
|
+
<th>ActiveRecord/ActiveSupport versions</th>
|
44
|
+
<td>>= 3</td>
|
45
|
+
<td>< 3</td>
|
46
|
+
</tr>
|
47
|
+
<tr>
|
48
|
+
<th>Ruby versions</th>
|
49
|
+
<td></td>
|
50
|
+
<td></td>
|
51
|
+
</tr>
|
52
|
+
<tr>
|
53
|
+
<td>MRI 1.9.3</td>
|
54
|
+
<td>OK</td>
|
55
|
+
<td>Untested</td>
|
56
|
+
</tr>
|
57
|
+
<tr>
|
58
|
+
<td>MRI 1.8.7</td>
|
59
|
+
<td>OK</td>
|
60
|
+
<td>Should be OK</td>
|
61
|
+
</tr>
|
62
|
+
</table>
|
8
63
|
|
9
|
-
|
64
|
+
### Important notes on MySQL support
|
10
65
|
|
11
|
-
|
66
|
+
#### Security warning
|
12
67
|
|
13
|
-
|
14
|
-
rake test
|
15
|
-
|
16
|
-
One test should fail with 'known issue with MySQL' (see commit 75da4b08).
|
68
|
+
Be sure to first read and understand the [security implications](http://dev.mysql.com/doc/refman/5.0/en/load-data-local.html) of `LOAD DATA LOCAL INFILE`. In particular, having this enabled on a web app is probably not a good idea.
|
17
69
|
|
18
|
-
####
|
70
|
+
#### v0.9.5
|
19
71
|
|
20
|
-
|
72
|
+
v0.9.5 will always use `LOAD DATA LOCAL INFILE` for bulk load - this is not configurable.
|
21
73
|
|
22
|
-
|
23
|
-
|
74
|
+
#### v1.0.0.rc1
|
75
|
+
|
76
|
+
This version should by default use `LOAD DATA INFILE` for safer defaults.
|
77
|
+
|
78
|
+
You can override this by passing `:local_infile => true` though.
|
79
|
+
|
80
|
+
#### Troubleshooting LOCAL INFILE
|
81
|
+
|
82
|
+
If you enable `:local_infile => true` and meet the following error, then you'll have to work it around.
|
83
|
+
|
84
|
+
```
|
85
|
+
The used command is not allowed with this MySQL version: LOAD DATA LOCAL INFILE
|
86
|
+
```
|
87
|
+
|
88
|
+
For mysql2 with AR >= 3.1:
|
89
|
+
|
90
|
+
- use [this fork](https://github.com/activewarehouse/mysql2) until [this pull-request](https://github.com/brianmario/mysql2/pull/242) is merged
|
91
|
+
- add `local_infile: true` to your database.yml
|
92
|
+
|
93
|
+
For mysql2 with AR < 3.1:
|
94
|
+
|
95
|
+
- no current easy solution afaik
|
96
|
+
|
97
|
+
For mysql:
|
98
|
+
|
99
|
+
- try this [untested patch](https://github.com/activewarehouse/adapter_extensions/issues/7)
|
100
|
+
|
101
|
+
### Notes on SQLServer support
|
102
|
+
|
103
|
+
v0.9.5 had a broken support for SQLServer.
|
104
|
+
|
105
|
+
v1.0.0.rc1 has a work-in-progress bulk import using `freebcp`. More tweaking needed.
|
106
|
+
|
107
|
+
### Running the tests
|
108
|
+
|
109
|
+
You may have to tweak the Rakefile and database.yml a bit, but roughly:
|
110
|
+
|
111
|
+
```
|
112
|
+
rake ci:create_db
|
113
|
+
rake "ci:run_one[mysql2,test/config/gemfiles/Gemfile.rails-3.2.x]"
|
114
|
+
```
|
115
|
+
|
116
|
+
You can also run a matrix of tests using:
|
117
|
+
|
118
|
+
```
|
119
|
+
rake ci:run_matrix
|
120
|
+
```
|
121
|
+
|
122
|
+
### License
|
123
|
+
|
124
|
+
MIT
|
125
|
+
|
126
|
+
### Contributors
|
127
|
+
|
128
|
+
* Thibaut Barrère (current maintainer)
|
129
|
+
* original code by Anthony Eden and probably others (let me know if you read this!)
|
130
|
+
* thanks to [Zach Dennis](https://github.com/zdennis/activerecord-import) for his work where I borrowed ideas for the rails 3 rewrite
|
data/Rakefile
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
2
3
|
require 'rake'
|
3
4
|
require 'rake/testtask'
|
4
|
-
require 'rdoc'
|
5
|
-
require 'rdoc/task'
|
6
5
|
|
7
6
|
desc 'Default: run unit tests.'
|
8
7
|
task :default => :test
|
@@ -15,63 +14,49 @@ Rake::TestTask.new(:test) do |t|
|
|
15
14
|
# TODO: reset the database
|
16
15
|
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
rm_f 'coverage.data'
|
22
|
-
mkdir 'coverage' unless File.exist?('coverage')
|
23
|
-
rcov = "rcov --aggregate coverage.data --text-summary -Ilib"
|
24
|
-
system("#{rcov} test/*_test.rb test/**/*_test.rb")
|
25
|
-
system("open coverage/index.html") if PLATFORM['darwin']
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
desc 'Generate documentation for the AdapterExtensions library.'
|
30
|
-
Rake::RDocTask.new(:rdoc) do |rdoc|
|
31
|
-
rdoc.rdoc_dir = 'rdoc'
|
32
|
-
rdoc.title = 'Extensions for Rails adapters'
|
33
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
34
|
-
rdoc.rdoc_files.include('README')
|
35
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
+
def system!(cmd)
|
18
|
+
puts cmd
|
19
|
+
raise "Command failed!" unless system(cmd)
|
36
20
|
end
|
37
21
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
22
|
+
# experimental tasks to reproduce the Travis behaviour locally
|
23
|
+
namespace :ci do
|
24
|
+
|
25
|
+
desc "Create required databases for tests (db in [mysql, mysql2, postgresql])"
|
26
|
+
task :create_db, :db do |t, args|
|
27
|
+
db = args[:db] || ENV['DB']
|
28
|
+
case db
|
29
|
+
when /mysql/;
|
30
|
+
# TODO - extract this info from database.yml
|
31
|
+
system! "mysql -e 'create database adapter_extensions_test;'"
|
32
|
+
system! "mysql adapter_extensions_test < test/config/databases/mysql_setup.sql"
|
33
|
+
when /postgres/;
|
34
|
+
system! "psql -c 'create database adapter_extensions_test;' -U postgres"
|
35
|
+
system! "psql -d adapter_extensions_test -U postgres -f test/config/databases/postgresql_setup.sql"
|
36
|
+
else abort("I don't know how to create the database for DB=#{db}!")
|
51
37
|
end
|
52
|
-
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
|
53
|
-
|
54
|
-
total_lines += lines
|
55
|
-
total_codelines += codelines
|
56
|
-
|
57
|
-
lines, codelines = 0, 0
|
58
38
|
end
|
59
39
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
40
|
+
desc "For current RVM, run the tests for one db and one gemfile"
|
41
|
+
task :run_one, :db, :gemfile do |t, args|
|
42
|
+
ENV['BUNDLE_GEMFILE'] = File.expand_path(args[:gemfile] || (File.dirname(__FILE__) + '/test/config/gemfiles/Gemfile.rails-3.2.x'))
|
43
|
+
ENV['DB'] = args[:db] || 'mysql2'
|
44
|
+
system! "bundle install && bundle exec rake"
|
45
|
+
end
|
66
46
|
|
67
|
-
for
|
68
|
-
|
69
|
-
|
70
|
-
|
47
|
+
desc "For current RVM, run the tests for all the combination in travis configuration"
|
48
|
+
task :run_matrix do
|
49
|
+
require 'cartesian'
|
50
|
+
config = YAML.load_file('.travis.yml')
|
51
|
+
config['env'].cartesian(config['gemfile']).each do |*x|
|
52
|
+
env, gemfile = *x.flatten
|
53
|
+
db = env.gsub('DB=', '')
|
54
|
+
print [db, gemfile].inspect.ljust(40) + ": "
|
55
|
+
cmd = "rake \"ci:run_one[#{db},#{gemfile}]\""
|
56
|
+
result = system "#{cmd} > /dev/null 2>&1"
|
57
|
+
result = result ? "OK" : "FAILED! - re-run with: #{cmd}"
|
58
|
+
puts result
|
59
|
+
end
|
71
60
|
end
|
72
|
-
end
|
73
61
|
|
74
|
-
desc "Publish the API documentation (UNTESTED CURRENTLY)"
|
75
|
-
task :pdoc => [:rdoc] do
|
76
|
-
Rake::SshDirPublisher.new("aeden@rubyforge.org", "/var/www/gforge-projects/activewarehouse/adapter_extensions/rdoc", "rdoc").upload
|
77
62
|
end
|
data/adapter_extensions.gemspec
CHANGED
@@ -18,8 +18,10 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.required_rubygems_version = ">= 1.3.6"
|
19
19
|
|
20
20
|
s.add_runtime_dependency('rake', '>= 0.8.3')
|
21
|
-
s.add_runtime_dependency('activesupport', '>=
|
22
|
-
s.add_runtime_dependency('activerecord', '>=
|
21
|
+
s.add_runtime_dependency('activesupport', '>= 3.0.0')
|
22
|
+
s.add_runtime_dependency('activerecord', '>= 3.0.0')
|
23
|
+
s.add_development_dependency('flexmock')
|
24
|
+
s.add_development_dependency('cartesian')
|
23
25
|
|
24
26
|
s.files = `git ls-files`.split("\n")
|
25
27
|
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module AdapterExtensions::AbstractAdapter
|
2
|
+
|
3
|
+
# Truncate the specified table - allow to pass an optional string
|
4
|
+
# to let the called add extra parameters like RESET IDENTITY for pg
|
5
|
+
def truncate(table_name, options=nil)
|
6
|
+
statement = [
|
7
|
+
'TRUNCATE TABLE',
|
8
|
+
table_name,
|
9
|
+
options
|
10
|
+
].compact.join(' ')
|
11
|
+
execute(statement)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Bulk loading interface. Load the data from the specified file into the
|
15
|
+
# given table. Note that options will be adapter-dependent.
|
16
|
+
def bulk_load(file, table_name, options={})
|
17
|
+
raise ArgumentError, "#{file} does not exist" unless File.exist?(file)
|
18
|
+
raise ArgumentError, "#{table_name} does not exist" unless tables.include?(table_name)
|
19
|
+
do_bulk_load(file, table_name, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# SQL select into statement constructs a new table from the results
|
23
|
+
# of a select. It is used to select data from a table and create a new
|
24
|
+
# table with its result set at the same time. Note that this method
|
25
|
+
# name does not necessarily match the implementation. E.g. MySQL's
|
26
|
+
# version of this is 'CREATE TABLE ... AS SELECT ...'
|
27
|
+
def support_select_into_table?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add a chunk of SQL to the given query that will create a new table and
|
32
|
+
# execute the select into that table.
|
33
|
+
def add_select_into_table(new_table_name, sql_query)
|
34
|
+
raise NotImplementedError, "add_select_into_table is an abstract method"
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
# for subclasses to implement
|
40
|
+
def do_bulk_load(file, table_name, options={})
|
41
|
+
raise NotImplementedError, "do_bulk_load is an abstract method"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module AdapterExtensions::MysqlAdapter
|
2
|
+
|
3
|
+
def support_select_into_table?
|
4
|
+
true
|
5
|
+
end
|
6
|
+
|
7
|
+
# Inserts an INTO table_name clause to the sql_query.
|
8
|
+
def add_select_into_table(new_table_name, sql_query)
|
9
|
+
"CREATE TABLE #{new_table_name} " + sql_query
|
10
|
+
end
|
11
|
+
|
12
|
+
# Copy the specified table.
|
13
|
+
def copy_table(old_table_name, new_table_name)
|
14
|
+
transaction do
|
15
|
+
execute "CREATE TABLE #{new_table_name} LIKE #{old_table_name}"
|
16
|
+
execute "INSERT INTO #{new_table_name} SELECT * FROM #{old_table_name}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def disable_keys(table)
|
21
|
+
execute("ALTER TABLE #{table} DISABLE KEYS")
|
22
|
+
end
|
23
|
+
|
24
|
+
def enable_keys(table)
|
25
|
+
execute("ALTER TABLE #{table} ENABLE KEYS")
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_keys_disabled(table)
|
29
|
+
disable_keys(table)
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
enable_keys(table)
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
# Call +bulk_load+, as that method wraps this method.
|
37
|
+
#
|
38
|
+
# Bulk load the data in the specified file.
|
39
|
+
#
|
40
|
+
# Options:
|
41
|
+
# * <tt>:ignore</tt> -- Ignore the specified number of lines from the source file
|
42
|
+
# * <tt>:columns</tt> -- Array of column names defining the source file column order
|
43
|
+
# * <tt>:fields</tt> -- Hash of options for fields:
|
44
|
+
# * <tt>:delimited_by</tt> -- The field delimiter
|
45
|
+
# * <tt>:enclosed_by</tt> -- The field enclosure
|
46
|
+
# * <tt>:replace</tt> -- Add in REPLACE to LOAD DATA INFILE command
|
47
|
+
# * <tt>:disable_keys</tt> -- if set to true, disable keys, loads, then enables again
|
48
|
+
# * <tt>:local_infile</tt>::
|
49
|
+
# if set to true, use LOAD DATA LOCAL INFILE rather than LOAD DATA INFILE
|
50
|
+
# see http://dev.mysql.com/doc/refman/5.0/en/load-data-local.html for security issues with this
|
51
|
+
def do_bulk_load(file, table_name, options={})
|
52
|
+
return if File.size(file) == 0
|
53
|
+
|
54
|
+
replace = options[:replace] ? 'REPLACE' : ''
|
55
|
+
local = options[:local_infile] ? 'LOCAL' : ''
|
56
|
+
q = "LOAD DATA #{local} INFILE '#{file}' #{replace} INTO TABLE #{table_name}"
|
57
|
+
if options[:fields]
|
58
|
+
q << " FIELDS"
|
59
|
+
q << " TERMINATED BY '#{options[:fields][:delimited_by]}'" if options[:fields][:delimited_by]
|
60
|
+
q << " ENCLOSED BY '#{options[:fields][:enclosed_by]}'" if options[:fields][:enclosed_by]
|
61
|
+
end
|
62
|
+
q << " IGNORE #{options[:ignore]} LINES" if options[:ignore]
|
63
|
+
q << " (#{options[:columns].map { |c| quote_column_name(c.to_s) }.join(',')})" if options[:columns]
|
64
|
+
|
65
|
+
if options[:disable_keys]
|
66
|
+
with_keys_disabled(table_name) { execute(q) }
|
67
|
+
else
|
68
|
+
execute(q)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module AdapterExtensions::PostgreSQLAdapter
|
2
|
+
|
3
|
+
def support_select_into_table?
|
4
|
+
true
|
5
|
+
end
|
6
|
+
|
7
|
+
# Inserts an INTO table_name clause to the sql_query.
|
8
|
+
def add_select_into_table(new_table_name, sql_query)
|
9
|
+
sql_query.sub(/FROM/i, "INTO #{new_table_name} FROM")
|
10
|
+
end
|
11
|
+
|
12
|
+
# Copy the specified table.
|
13
|
+
def copy_table(old_table_name, new_table_name)
|
14
|
+
execute add_select_into_table(new_table_name, "SELECT * FROM #{old_table_name}")
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
# Call +bulk_load+, as that method wraps this method.
|
19
|
+
#
|
20
|
+
# Bulk load the data in the specified file.
|
21
|
+
#
|
22
|
+
# Options:
|
23
|
+
# * <tt>:ignore</tt> -- Ignore the specified number of lines from the source file. In the case of PostgreSQL
|
24
|
+
# only the first line will be ignored from the source file regardless of the number of lines specified.
|
25
|
+
# * <tt>:columns</tt> -- Array of column names defining the source file column order
|
26
|
+
# * <tt>:fields</tt> -- Hash of options for fields:
|
27
|
+
# * <tt>:delimited_by</tt> -- The field delimiter
|
28
|
+
# * <tt>:null_string</tt> -- The string that should be interpreted as NULL (in addition to \N)
|
29
|
+
# * <tt>:enclosed_by</tt> -- The field enclosure
|
30
|
+
def do_bulk_load(file, table_name, options={})
|
31
|
+
q = "COPY #{table_name} "
|
32
|
+
q << "(#{options[:columns].join(',')}) " if options[:columns]
|
33
|
+
q << "FROM '#{File.expand_path(file)}' "
|
34
|
+
if options[:fields]
|
35
|
+
q << "WITH "
|
36
|
+
q << "DELIMITER '#{options[:fields][:delimited_by]}' " if options[:fields][:delimited_by]
|
37
|
+
q << "NULL '#{options[:fields][:null_string]}'" if options[:fields][:null_string]
|
38
|
+
if options[:fields][:enclosed_by] || options[:ignore] && options[:ignore] > 0
|
39
|
+
q << "CSV "
|
40
|
+
q << "HEADER " if options[:ignore] && options[:ignore] > 0
|
41
|
+
q << "QUOTE '#{options[:fields][:enclosed_by]}' " if options[:fields][:enclosed_by]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
execute(q)
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module AdapterExtensions::SQLServerAdapter
|
2
|
+
|
3
|
+
def support_select_into_table?
|
4
|
+
true
|
5
|
+
end
|
6
|
+
|
7
|
+
# Inserts an INTO table_name clause to the sql_query.
|
8
|
+
def add_select_into_table(new_table_name, sql_query)
|
9
|
+
sql_query.sub(/FROM/i, "INTO #{new_table_name} FROM")
|
10
|
+
end
|
11
|
+
|
12
|
+
# Copy the specified table.
|
13
|
+
def copy_table(old_table_name, new_table_name)
|
14
|
+
execute add_select_into_table(new_table_name, "SELECT * FROM #{old_table_name}")
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
# Call +bulk_load+, as that method wraps this method.
|
19
|
+
#
|
20
|
+
# Bulk load the data in the specified file. This implementation relies
|
21
|
+
# on freebcp being in your PATH.
|
22
|
+
#
|
23
|
+
# Currently supported options:
|
24
|
+
# * <tt>:bin</tt> -- alternate path to freebcp
|
25
|
+
# * <tt>:max_errors</tt> -- maximum number of errors (freebcp -m parameter)
|
26
|
+
# * <tt>:env</tt> -- override ActiveRecord environment to be used
|
27
|
+
# * <tt>:fields</tt> -- Hash of options for fields:
|
28
|
+
# * <tt>:delimited_by</tt> -- The field delimiter
|
29
|
+
# :columns is currently unsupported and will raise an exception
|
30
|
+
def do_bulk_load(filename, table_name, options={})
|
31
|
+
env_name = options[:env] || Rails.env
|
32
|
+
config = ActiveRecord::Base.configurations[env_name]
|
33
|
+
|
34
|
+
raise NotImplementedError.new(":columns option is not currently supported") if options[:columns]
|
35
|
+
|
36
|
+
# work in progress.
|
37
|
+
# - http://linux.die.net/man/1/freebcp
|
38
|
+
# - http://stackoverflow.com/a/924943/20302
|
39
|
+
# - http://msdn.microsoft.com/en-us/library/ms162802.aspx
|
40
|
+
# - http://www.dbforums.com/microsoft-sql-server/1624618-how-bring-out-column-names-bcp.html
|
41
|
+
|
42
|
+
command = []
|
43
|
+
command << (options[:bin] || 'freebcp')
|
44
|
+
command << "\"#{config['database']}.dbo.#{table_name}\""
|
45
|
+
command << "in \"#{filename}\""
|
46
|
+
command << "-S \"#{config['host']}\""
|
47
|
+
command << "-U \"#{config['username']}\""
|
48
|
+
command << "-P \"#{config['password']}\""
|
49
|
+
command << "-c" # character mode
|
50
|
+
command << "-t \"#{options[:fields][:delimited_by]}\"" if options[:fields] && options[:fields][:delimited_by]
|
51
|
+
command << "-b 10000" # bulk size
|
52
|
+
|
53
|
+
command << "-m #{options[:max_errors]}" if options[:max_errors]
|
54
|
+
command << "-e \"#{filename}.in.errors\""
|
55
|
+
command = command.join(' ')
|
56
|
+
|
57
|
+
# left-overs from legacy bcp call - must see if they must remain or not
|
58
|
+
# -a8192 -q -E
|
59
|
+
|
60
|
+
# TODO - raise a better exception here
|
61
|
+
raise "bulk load failed!" unless Kernel.system(command)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "active_record"
|
3
|
+
require "active_record/version"
|
4
|
+
|
5
|
+
module AdapterExtensions
|
6
|
+
AdapterPath = File.join File.expand_path(File.dirname(__FILE__)), "/active_record/adapters"
|
7
|
+
|
8
|
+
# Loads the extensions for a specific database adapter
|
9
|
+
def self.require_adapter(adapter)
|
10
|
+
require File.join(AdapterPath,"/abstract_adapter")
|
11
|
+
specific_adapter = File.join(AdapterPath,"/#{adapter}_adapter")
|
12
|
+
require specific_adapter if File.exists?(specific_adapter + '.rb')
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load_from_connection_pool(connection_pool)
|
16
|
+
require_adapter connection_pool.spec.config[:adapter]
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/lib/adapter_extensions.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Requiring this file will require all of the necessary files to function.
|
1
|
+
require 'adapter_extensions/base'
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
class ActiveRecord::Base
|
4
|
+
class << self
|
5
|
+
# for 1.8.7, modules loaded more than once that use alias_method_chain will
|
6
|
+
# cause stack level too deep errors - make sure we avoid this
|
7
|
+
unless self.instance_methods.include?('establish_connection_without_adapter_extensions')
|
8
|
+
def establish_connection_with_adapter_extensions(*args)
|
9
|
+
establish_connection_without_adapter_extensions(*args)
|
10
|
+
ActiveSupport.run_load_hooks(:active_record_connection_established, connection_pool)
|
11
|
+
end
|
12
|
+
alias_method_chain :establish_connection, :adapter_extensions
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
8
16
|
|
9
|
-
|
10
|
-
|
17
|
+
ActiveSupport.on_load(:active_record_connection_established) do |connection_pool|
|
18
|
+
AdapterExtensions.load_from_connection_pool connection_pool
|
19
|
+
end
|