bulk-insert-active-record 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -17
- data/.rubocop.yml +6 -0
- data/.ruby-gemset +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +56 -0
- data/README.md +9 -11
- data/Rakefile +1 -1
- data/bulk-insert-active-record.gemspec +15 -14
- data/lib/bulk-insert-active-record.rb +12 -13
- data/lib/inserters.rb +5 -7
- data/lib/inserters/base.rb +13 -26
- data/lib/inserters/oracle.rb +13 -12
- metadata +50 -6
- data/lib/inserters/sequenced.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 002fae46c420ba556c11d06b5dbd505de1e4ffac
|
4
|
+
data.tar.gz: 3564bc97c838fae38bf920b713a10f3f5ac371a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df5dce359603164410f145d8a6b661e3b89fcb8ba49dd0ed6c47d0a096b66a7ca5f4aeb71bf92b26ee43079fc4505267c10f8d81c34a7f67ec21ea4baf322ad5
|
7
|
+
data.tar.gz: 0b48d16f0e2c49bca418e8a2afcc26d8d016f0ffe3648feaa0eec3b1cc77c710a27f601f21ff066863172d6906769129a1ebfe67cb4dd14d03691f7be673662b
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/.ruby-gemset
CHANGED
@@ -1 +1 @@
|
|
1
|
-
bulk-insert-active-record
|
1
|
+
bulk-insert-active-record
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
bulk-insert-active-record (0.0.4)
|
5
|
+
activerecord (>= 3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.2.6)
|
11
|
+
activesupport (= 4.2.6)
|
12
|
+
builder (~> 3.1)
|
13
|
+
activerecord (4.2.6)
|
14
|
+
activemodel (= 4.2.6)
|
15
|
+
activesupport (= 4.2.6)
|
16
|
+
arel (~> 6.0)
|
17
|
+
activesupport (4.2.6)
|
18
|
+
i18n (~> 0.7)
|
19
|
+
json (~> 1.7, >= 1.7.7)
|
20
|
+
minitest (~> 5.1)
|
21
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
22
|
+
tzinfo (~> 1.1)
|
23
|
+
arel (6.0.3)
|
24
|
+
ast (2.2.0)
|
25
|
+
builder (3.2.2)
|
26
|
+
i18n (0.7.0)
|
27
|
+
json (1.8.3)
|
28
|
+
minitest (5.9.0)
|
29
|
+
parser (2.3.1.0)
|
30
|
+
ast (~> 2.2)
|
31
|
+
powerpack (0.1.1)
|
32
|
+
rainbow (2.1.0)
|
33
|
+
rake (11.1.2)
|
34
|
+
rubocop (0.40.0)
|
35
|
+
parser (>= 2.3.1.0, < 3.0)
|
36
|
+
powerpack (~> 0.1)
|
37
|
+
rainbow (>= 1.99.1, < 3.0)
|
38
|
+
ruby-progressbar (~> 1.7)
|
39
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
40
|
+
ruby-progressbar (1.8.1)
|
41
|
+
thread_safe (0.3.5)
|
42
|
+
tzinfo (1.2.2)
|
43
|
+
thread_safe (~> 0.1)
|
44
|
+
unicode-display_width (1.0.5)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
bulk-insert-active-record!
|
51
|
+
bundler (~> 1.12)
|
52
|
+
rake
|
53
|
+
rubocop
|
54
|
+
|
55
|
+
BUNDLED WITH
|
56
|
+
1.12.4
|
data/README.md
CHANGED
@@ -1,27 +1,25 @@
|
|
1
1
|
# Bulk Insert Active Record (for ActiveRecord 3 or higher)
|
2
2
|
|
3
|
-
This gem extends Active Record and allows you to insert multiple records in one SQL statement,
|
4
|
-
|
3
|
+
This gem extends Active Record and allows you to insert multiple records in one SQL statement, dramatically increasing
|
4
|
+
performance.
|
5
5
|
|
6
6
|
## Current status
|
7
7
|
|
8
|
-
This gem is currently actively in development, but because it's already useful, I've decided to
|
9
|
-
|
10
|
-
via JDBC.
|
8
|
+
This gem is currently actively in development, but because it's already useful, I've decided to publish it in its
|
9
|
+
premature state. Right now it has been manually tested with MySQL and SQL Server via JDBC.
|
11
10
|
|
12
11
|
## Usage
|
13
12
|
|
14
|
-
Installation is done by adding the gem your *Gemfile* and running Bundler. After loading Active
|
15
|
-
|
13
|
+
Installation is done by adding the gem your *Gemfile* and running Bundler. After loading Active Record an extra class
|
14
|
+
method is available for your models.
|
16
15
|
|
17
16
|
## Examples
|
18
17
|
|
19
|
-
Suppose you want to insert multiple Post records in the database. This can be done in different
|
20
|
-
ways:
|
18
|
+
Suppose you want to insert multiple Post records in the database. This can be done in different ways:
|
21
19
|
|
22
20
|
# suppose it has 3 columns (`id`, `author` and `text`)
|
23
21
|
class Post < ActiveRecord::Base
|
24
|
-
end
|
22
|
+
end
|
25
23
|
|
26
24
|
# create an array of arrays
|
27
25
|
posts = [
|
@@ -36,4 +34,4 @@ ways:
|
|
36
34
|
|
37
35
|
# Copyright
|
38
36
|
|
39
|
-
©
|
37
|
+
© 2016 Walter Horstman, [IT on Rails](http://itonrails.com)
|
data/Rakefile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require('bundler/gem_tasks')
|
@@ -1,22 +1,23 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name
|
7
|
-
spec.
|
8
|
-
spec.
|
9
|
-
spec.
|
10
|
-
spec.required_ruby_version = '>= 1.9.2'
|
6
|
+
spec.name = 'bulk-insert-active-record'
|
7
|
+
spec.version = '0.0.4'
|
8
|
+
spec.author = ['Walter Horstman']
|
9
|
+
spec.email = ['walter.horstman@itonrails.com']
|
11
10
|
|
12
|
-
spec.
|
13
|
-
spec.
|
14
|
-
spec.homepage
|
11
|
+
spec.summary = 'Lightweight bulk insert mechanism for ActiveRecord 3 or higher'
|
12
|
+
spec.description = 'This gem allows you to insert multiple rows as once, dramatically increasing performance.'
|
13
|
+
spec.homepage = 'https://github.com/walterhorstman/bulk-insert-active-record'
|
15
14
|
|
16
|
-
spec.files
|
17
|
-
spec.executables
|
18
|
-
spec.
|
19
|
-
spec.require_path = 'lib'
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.require_paths = ['lib']
|
20
18
|
|
21
19
|
spec.add_dependency('activerecord', '>= 3')
|
22
|
-
|
20
|
+
spec.add_development_dependency('bundler', '~> 1.12')
|
21
|
+
spec.add_development_dependency('rake')
|
22
|
+
spec.add_development_dependency('rubocop')
|
23
|
+
end
|
@@ -1,32 +1,30 @@
|
|
1
|
+
# rubocop:disable Style/FileName
|
1
2
|
require_relative('inserters')
|
2
3
|
|
4
|
+
# Extends the given base class (a subclass of ActiveRecord::Base) with a bulk_insert() class method.
|
3
5
|
module BulkInsertActiveRecord
|
4
|
-
|
6
|
+
# rubocop:disable Metrics/MethodLength
|
5
7
|
def self.included(base)
|
6
8
|
base.class_eval do
|
9
|
+
def self.bulk_insert(records, columns = column_names)
|
10
|
+
raise('No connection with the database') unless connection.active?
|
7
11
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
inserter = Inserters::factory(self)
|
12
|
-
self.transaction do
|
12
|
+
inserter = Inserters.factory(self)
|
13
|
+
transaction do
|
13
14
|
if inserter.nil?
|
14
|
-
|
15
|
+
insert_one_by_one(records, columns)
|
15
16
|
else
|
16
|
-
|
17
|
-
self.connection.insert(sql)
|
17
|
+
inserter.execute(records, columns)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
private
|
23
|
-
|
24
22
|
def self.insert_one_by_one(records, column_names)
|
25
23
|
records.each do |record|
|
26
24
|
if record.is_a?(self)
|
27
25
|
record.save
|
28
26
|
else
|
29
|
-
item =
|
27
|
+
item = new
|
30
28
|
column_names.each_with_index do |column_name, index|
|
31
29
|
item[column_name] = record[index]
|
32
30
|
end
|
@@ -36,8 +34,9 @@ module BulkInsertActiveRecord
|
|
36
34
|
end
|
37
35
|
end
|
38
36
|
end
|
37
|
+
# rubocop:enable Metrics/MethodLength
|
39
38
|
end
|
40
39
|
|
41
40
|
ActiveSupport.on_load(:active_record) do
|
42
41
|
include(BulkInsertActiveRecord)
|
43
|
-
end
|
42
|
+
end
|
data/lib/inserters.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
Dir[File.expand_path('../inserters/*.rb', __FILE__)].each { |file_name| require(file_name) }
|
2
2
|
|
3
3
|
module BulkInsertActiveRecord
|
4
|
+
# Inserters module
|
4
5
|
module Inserters
|
5
|
-
|
6
|
-
def self.factory(active_record_class, options = {})
|
6
|
+
def self.factory(active_record_class)
|
7
7
|
inserter_class = case active_record_class.connection.adapter_name.downcase
|
8
8
|
when 'mssql', 'mysql', 'sqlserver' then Base
|
9
|
-
|
10
|
-
# when 'oracle' then Oracle
|
11
|
-
else nil
|
9
|
+
when 'oracle' then Oracle
|
12
10
|
end
|
13
11
|
|
14
|
-
inserter_class.nil? ? nil : inserter_class.new(active_record_class
|
12
|
+
inserter_class.nil? ? nil : inserter_class.new(active_record_class)
|
15
13
|
end
|
16
14
|
end
|
17
|
-
end
|
15
|
+
end
|
data/lib/inserters/base.rb
CHANGED
@@ -1,36 +1,23 @@
|
|
1
|
-
# This base implementation works for MySQL and SQLServer
|
2
1
|
module BulkInsertActiveRecord
|
3
2
|
module Inserters
|
3
|
+
# This base implementation works for MySQL and SQLServer
|
4
4
|
class Base
|
5
|
-
|
6
|
-
def initialize(active_record_class, options = {})
|
5
|
+
def initialize(active_record_class)
|
7
6
|
@connection = active_record_class.connection
|
8
7
|
@quoted_table_name = active_record_class.quoted_table_name
|
9
|
-
|
10
|
-
# basic bulk insert statement
|
11
|
-
@statement = options[:statement] || 'INSERT INTO %{table_name}(%{columns_clause}) VALUES %{values_clause}'
|
12
|
-
# character(s) used to separate columns in the columns_clause of the statement
|
13
|
-
@column_separator = options[:column_separator] || ','
|
14
|
-
# character(s) used to separate records in the values_clause of the statement
|
15
|
-
@record_separator = options[:record_separator] || ','
|
16
|
-
# sql fragment for individual records in the values_clause of the statement
|
17
|
-
@record_statement = options[:record_statement] || '(%{value_clause})'
|
18
|
-
# character(s) used to separate values in the value_clause of the record statement
|
19
|
-
@value_separator = options[:value_separator] || ','
|
20
8
|
end
|
21
9
|
|
22
|
-
|
23
|
-
|
24
|
-
@statement
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
}
|
10
|
+
def execute(records, column_names)
|
11
|
+
statement = 'INSERT INTO %{table_name}(%{columns_clause}) VALUES(%{values_clause})'
|
12
|
+
@connection.insert(format(statement, table_name: @quoted_table_name,
|
13
|
+
columns_clause: column_names.map do |column_name|
|
14
|
+
@connection.quote_column_name(column_name)
|
15
|
+
end.join(','),
|
16
|
+
values_clause: records.map do |record|
|
17
|
+
value_clause = record.map { |value| @connection.quote(value) }.join(',')
|
18
|
+
"(#{value_clause})"
|
19
|
+
end.join(',')))
|
33
20
|
end
|
34
21
|
end
|
35
22
|
end
|
36
|
-
end
|
23
|
+
end
|
data/lib/inserters/oracle.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
|
-
require_relative('sequenced')
|
2
|
-
|
3
1
|
module BulkInsertActiveRecord
|
4
2
|
module Inserters
|
5
|
-
|
6
|
-
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
# Implementation specific for Oracle
|
4
|
+
class Oracle < Base
|
5
|
+
def execute(records, column_names) # override
|
6
|
+
statement = 'BEGIN INSERT INTO %{table_name}(%{columns_clause}) VALUES(%{values_clause}); END;'
|
7
|
+
@connection.execute(format(statement, table_name: @quoted_table_name,
|
8
|
+
columns_clause: column_names.map do |column_name|
|
9
|
+
@connection.quote_column_name(column_name)
|
10
|
+
end.join(','),
|
11
|
+
values_clause: records.map do |record|
|
12
|
+
value_clause = record.map { |value| @connection.quote(value) }.join(',')
|
13
|
+
"SELECT #{value_clause} FROM dual"
|
14
|
+
end.join(' UNION ')))
|
14
15
|
end
|
15
16
|
end
|
16
17
|
end
|
17
|
-
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bulk-insert-active-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Walter Horstman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -24,16 +24,61 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.12'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.12'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
27
69
|
description: This gem allows you to insert multiple rows as once, dramatically increasing
|
28
70
|
performance.
|
29
|
-
email:
|
71
|
+
email:
|
72
|
+
- walter.horstman@itonrails.com
|
30
73
|
executables: []
|
31
74
|
extensions: []
|
32
75
|
extra_rdoc_files: []
|
33
76
|
files:
|
34
77
|
- ".gitignore"
|
78
|
+
- ".rubocop.yml"
|
35
79
|
- ".ruby-gemset"
|
36
80
|
- Gemfile
|
81
|
+
- Gemfile.lock
|
37
82
|
- README.md
|
38
83
|
- Rakefile
|
39
84
|
- bulk-insert-active-record.gemspec
|
@@ -41,7 +86,6 @@ files:
|
|
41
86
|
- lib/inserters.rb
|
42
87
|
- lib/inserters/base.rb
|
43
88
|
- lib/inserters/oracle.rb
|
44
|
-
- lib/inserters/sequenced.rb
|
45
89
|
homepage: https://github.com/walterhorstman/bulk-insert-active-record
|
46
90
|
licenses: []
|
47
91
|
metadata: {}
|
@@ -53,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
97
|
requirements:
|
54
98
|
- - ">="
|
55
99
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
100
|
+
version: '0'
|
57
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
102
|
requirements:
|
59
103
|
- - ">="
|
@@ -61,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
105
|
version: '0'
|
62
106
|
requirements: []
|
63
107
|
rubyforge_project:
|
64
|
-
rubygems_version: 2.
|
108
|
+
rubygems_version: 2.5.1
|
65
109
|
signing_key:
|
66
110
|
specification_version: 4
|
67
111
|
summary: Lightweight bulk insert mechanism for ActiveRecord 3 or higher
|
data/lib/inserters/sequenced.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require_relative('base')
|
2
|
-
|
3
|
-
module BulkInsertActiveRecord
|
4
|
-
module Inserters
|
5
|
-
class Sequenced < Base
|
6
|
-
|
7
|
-
def initialize(active_record_class, options = {})
|
8
|
-
@primary_key_name = active_record_class.primary_key
|
9
|
-
# SQL fragment defining how a sequenced value should be constructed
|
10
|
-
@sequence_statement = options[:sequence_statement]
|
11
|
-
super(active_record_class, options)
|
12
|
-
end
|
13
|
-
|
14
|
-
# returns bulk insert statement
|
15
|
-
def statement(records, column_names)
|
16
|
-
column_names.map!(&:to_s)
|
17
|
-
# if a primary key column wasn't specified, add it manually
|
18
|
-
add_primary_key = column_names.exclude?(@primary_key_name)
|
19
|
-
column_names << @primary_key_name if add_primary_key
|
20
|
-
# save position within each row array of the primary key
|
21
|
-
primary_key_index = column_names.index(@primary_key_name)
|
22
|
-
|
23
|
-
@statement % {
|
24
|
-
table_name: @quoted_table_name,
|
25
|
-
columns_clause: column_names.map { |column_name| @connection.quote_column_name(column_name) }.join(@column_separator),
|
26
|
-
values_clause: records.map do |record|
|
27
|
-
# if we need to manually added the primary key, add it to the end of the row array
|
28
|
-
if add_primary_key
|
29
|
-
record << @sequence_statement
|
30
|
-
else
|
31
|
-
record[primary_key_index] = (record[primary_key_index] == nil) ? @sequence_statement : @connection.quote(record[primary_key_index])
|
32
|
-
end
|
33
|
-
|
34
|
-
@record_statement % {
|
35
|
-
value_clause: record.map.with_index { |value, index| (index == primary_key_index) ? value : @connection.quote(value) }.join(@value_separator)
|
36
|
-
}
|
37
|
-
end.join(@record_separator)
|
38
|
-
}
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|