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.
Files changed (38) hide show
  1. data/.travis.yml +14 -0
  2. data/CHANGELOG +5 -0
  3. data/HOW_TO_RELEASE +4 -4
  4. data/LICENSE +1 -1
  5. data/README.md +121 -14
  6. data/Rakefile +38 -53
  7. data/adapter_extensions.gemspec +4 -2
  8. data/lib/adapter_extensions/active_record/adapters/abstract_adapter.rb +9 -0
  9. data/lib/adapter_extensions/active_record/adapters/mysql2_adapter.rb +6 -0
  10. data/lib/adapter_extensions/active_record/adapters/mysql_adapter.rb +6 -0
  11. data/lib/adapter_extensions/active_record/adapters/postgresql_adapter.rb +6 -0
  12. data/lib/adapter_extensions/active_record/adapters/sqlserver_adapter.rb +6 -0
  13. data/lib/adapter_extensions/adapters/abstract_adapter.rb +44 -0
  14. data/lib/adapter_extensions/adapters/mysql_adapter.rb +73 -0
  15. data/lib/adapter_extensions/adapters/postgresql_adapter.rb +47 -0
  16. data/lib/adapter_extensions/adapters/sqlserver_adapter.rb +63 -0
  17. data/lib/adapter_extensions/base.rb +19 -0
  18. data/lib/adapter_extensions/version.rb +1 -1
  19. data/lib/adapter_extensions.rb +17 -8
  20. data/test/abstract_adapter_test.rb +10 -4
  21. data/test/config/database.yml +15 -0
  22. data/test/{connection/mysql/setup.sql → config/databases/mysql_setup.sql} +0 -0
  23. data/test/{connection/postgresql/setup.sql → config/databases/postgresql_setup.sql} +0 -0
  24. data/test/config/gemfiles/.gitignore +1 -0
  25. data/test/config/gemfiles/Gemfile.rails-3.0.x +3 -0
  26. data/test/config/gemfiles/Gemfile.rails-3.1.x +3 -0
  27. data/test/config/gemfiles/Gemfile.rails-3.2.x +3 -0
  28. data/test/config/gemfiles/common.rb +22 -0
  29. data/test/integration/adapter_test.rb +23 -7
  30. data/test/test_helper.rb +3 -4
  31. data/test/unit/sqlserver_test_ignored.rb +89 -0
  32. metadata +86 -88
  33. data/lib/adapter_extensions/connection_adapters/abstract_adapter.rb +0 -50
  34. data/lib/adapter_extensions/connection_adapters/mysql_adapter.rb +0 -90
  35. data/lib/adapter_extensions/connection_adapters/postgresql_adapter.rb +0 -52
  36. data/lib/adapter_extensions/connection_adapters/sqlserver_adapter.rb +0 -44
  37. data/test/connection/mysql/connection.rb +0 -27
  38. 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
- * then use bundler to build + git tag + push to rubygems
3
+ * build the gem:
4
4
 
5
- rake release
5
+ gem build adapter_extensions.gemspec
6
6
 
7
- * if you remain stuck at "Pushed git commits and tags", the task may silently wait for your password. Check this if it's the case:
7
+ * create tag and push to rubygem:
8
8
 
9
- https://github.com/carlhuda/bundler/issues/980
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
- This library provides extensions to Rails' ActiveRecord adapters.
1
+ AdapterExtensions add extra abilities to Rails ActiveRecord adapters, including:
2
2
 
3
- As of version 0.9.5, adapter_extensions has dependencies on ActiveSupport and ActiveRecord 2.1.x or higher.
3
+ * bulk load
4
+ * truncate table
5
+ * copy table
6
+ * add select into table
7
+
8
+ ### Compatibility matrix
4
9
 
5
- ### How to test
10
+ See Travis for up-to-date info: [![Build Status](https://secure.travis-ci.org/activewarehouse/adapter_extensions.png?branch=master)](http://travis-ci.org/activewarehouse/adapter_extensions)
6
11
 
7
- Currently tested on MySQL and Postgres.
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>&gt;= 3</td>
45
+ <td>&lt; 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
- #### Testing on MySQL
64
+ ### Important notes on MySQL support
10
65
 
11
- see `test/connection/mysql/connection.rb` and tweek if needed, then:
66
+ #### Security warning
12
67
 
13
- mysql -u root -p -e "create database adapter_extensions_unittest"
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
- #### Testing on Postgresql
70
+ #### v0.9.5
19
71
 
20
- see `test/connection/postgresql/connection.rb` and tweek if needed, then:
72
+ v0.9.5 will always use `LOAD DATA LOCAL INFILE` for bulk load - this is not configurable.
21
73
 
22
- createdb adapter_extensions_unittest
23
- rake test DB=postgresql
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
- namespace :rcov do
19
- desc 'Measures test coverage'
20
- task :test do
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
- desc "Generate code statistics"
39
- task :lines do
40
- lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
41
-
42
- for file_name in FileList["lib/**/*.rb"]
43
- next if file_name =~ /vendor/
44
- f = File.open(file_name)
45
-
46
- while line = f.gets
47
- lines += 1
48
- next if line =~ /^\s*$/
49
- next if line =~ /^\s*#/
50
- codelines += 1
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
- puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
61
- end
62
-
63
- desc "Publish the release files to RubyForge (UNTESTED CURRENTLY)"
64
- task :release => [ :package ] do
65
- `rubyforge login`
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 ext in %w( gem tgz zip )
68
- release_command = "rubyforge add_release activewarehouse #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
69
- puts release_command
70
- system(release_command)
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
@@ -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', '>= 2.1.0')
22
- s.add_runtime_dependency('activerecord', '>= 2.1.0')
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,9 @@
1
+ require "adapter_extensions/adapters/abstract_adapter"
2
+
3
+ module ActiveRecord # :nodoc:
4
+ module ConnectionAdapters # :nodoc:
5
+ class AbstractAdapter # :nodoc:
6
+ include AdapterExtensions::AbstractAdapter
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ require "active_record/connection_adapters/mysql2_adapter"
2
+ require "adapter_extensions/adapters/mysql_adapter"
3
+
4
+ class ActiveRecord::ConnectionAdapters::Mysql2Adapter
5
+ include AdapterExtensions::MysqlAdapter
6
+ end
@@ -0,0 +1,6 @@
1
+ require "active_record/connection_adapters/mysql_adapter"
2
+ require "adapter_extensions/adapters/mysql_adapter"
3
+
4
+ class ActiveRecord::ConnectionAdapters::MysqlAdapter
5
+ include AdapterExtensions::MysqlAdapter
6
+ end
@@ -0,0 +1,6 @@
1
+ require "active_record/connection_adapters/postgresql_adapter"
2
+ require "adapter_extensions/adapters/postgresql_adapter"
3
+
4
+ class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
5
+ include AdapterExtensions::PostgreSQLAdapter
6
+ end
@@ -0,0 +1,6 @@
1
+ require "activerecord-sqlserver-adapter"
2
+ require "adapter_extensions/adapters/sqlserver_adapter"
3
+
4
+ class ActiveRecord::ConnectionAdapters::SQLServerAdapter
5
+ include AdapterExtensions::SQLServerAdapter
6
+ end
@@ -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
@@ -1,4 +1,4 @@
1
1
  # Source file identifying the version of AdapterExtensions in this package
2
2
  module AdapterExtensions#:nodoc:
3
- VERSION = "0.9.5"
3
+ VERSION = "1.0.0.rc1"
4
4
  end
@@ -1,10 +1,19 @@
1
- # Extensions to the Rails ActiveRecord adapters.
2
- #
3
- # Requiring this file will require all of the necessary files to function.
1
+ require 'adapter_extensions/base'
4
2
 
5
- require 'rubygems'
6
- require 'active_support'
7
- require 'active_record'
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
- $:.unshift(File.dirname(__FILE__))
10
- Dir[File.dirname(__FILE__) + "/adapter_extensions/**/*.rb"].each { |file| require(file) }
17
+ ActiveSupport.on_load(:active_record_connection_established) do |connection_pool|
18
+ AdapterExtensions.load_from_connection_pool connection_pool
19
+ end