bulk-insert-active-record 0.0.2 → 0.0.4
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.
- 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
|