activerecord-import 0.27.0 → 1.4.1
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 +5 -5
- data/.github/workflows/test.yaml +107 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +74 -8
- data/Brewfile +3 -1
- data/CHANGELOG.md +175 -2
- data/Gemfile +13 -9
- data/LICENSE +21 -56
- data/README.markdown +525 -21
- data/Rakefile +2 -0
- data/activerecord-import.gemspec +6 -5
- data/benchmarks/benchmark.rb +7 -1
- data/benchmarks/lib/base.rb +2 -0
- data/benchmarks/lib/cli_parser.rb +3 -1
- data/benchmarks/lib/float.rb +2 -0
- data/benchmarks/lib/mysql2_benchmark.rb +2 -0
- data/benchmarks/lib/output_to_csv.rb +2 -0
- data/benchmarks/lib/output_to_html.rb +4 -2
- data/benchmarks/models/test_innodb.rb +2 -0
- data/benchmarks/models/test_memory.rb +2 -0
- data/benchmarks/models/test_myisam.rb +2 -0
- data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +2 -0
- data/gemfiles/4.2.gemfile +2 -0
- data/gemfiles/5.0.gemfile +2 -0
- data/gemfiles/5.1.gemfile +2 -0
- data/gemfiles/5.2.gemfile +2 -0
- data/gemfiles/6.0.gemfile +4 -0
- data/gemfiles/6.1.gemfile +4 -0
- data/gemfiles/7.0.gemfile +4 -0
- data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +6 -4
- data/lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/jdbcsqlite3_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/postgresql_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +2 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +9 -1
- data/lib/activerecord-import/adapters/em_mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/adapters/mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/adapters/mysql_adapter.rb +10 -11
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +49 -38
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +21 -25
- data/lib/activerecord-import/base.rb +11 -2
- data/lib/activerecord-import/import.rb +180 -78
- data/lib/activerecord-import/mysql2.rb +2 -0
- data/lib/activerecord-import/postgresql.rb +2 -0
- data/lib/activerecord-import/sqlite3.rb +2 -0
- data/lib/activerecord-import/synchronize.rb +4 -2
- data/lib/activerecord-import/value_sets_parser.rb +4 -0
- data/lib/activerecord-import/version.rb +3 -1
- data/lib/activerecord-import.rb +3 -1
- data/test/adapters/jdbcmysql.rb +2 -0
- data/test/adapters/jdbcpostgresql.rb +2 -0
- data/test/adapters/jdbcsqlite3.rb +2 -0
- data/test/adapters/makara_postgis.rb +2 -0
- data/test/adapters/mysql2.rb +2 -0
- data/test/adapters/mysql2_makara.rb +2 -0
- data/test/adapters/mysql2spatial.rb +2 -0
- data/test/adapters/postgis.rb +2 -0
- data/test/adapters/postgresql.rb +2 -0
- data/test/adapters/postgresql_makara.rb +2 -0
- data/test/adapters/seamless_database_pool.rb +2 -0
- data/test/adapters/spatialite.rb +2 -0
- data/test/adapters/sqlite3.rb +2 -0
- data/test/{travis → github}/database.yml +3 -1
- data/test/import_test.rb +138 -4
- data/test/jdbcmysql/import_test.rb +2 -0
- data/test/jdbcpostgresql/import_test.rb +2 -0
- data/test/jdbcsqlite3/import_test.rb +2 -0
- data/test/makara_postgis/import_test.rb +2 -0
- data/test/models/account.rb +2 -0
- data/test/models/alarm.rb +2 -0
- data/test/models/animal.rb +8 -0
- data/test/models/bike_maker.rb +2 -0
- data/test/models/book.rb +2 -0
- data/test/models/car.rb +2 -0
- data/test/models/card.rb +5 -0
- data/test/models/chapter.rb +2 -0
- data/test/models/customer.rb +8 -0
- data/test/models/deck.rb +8 -0
- data/test/models/dictionary.rb +2 -0
- data/test/models/discount.rb +2 -0
- data/test/models/end_note.rb +2 -0
- data/test/models/group.rb +2 -0
- data/test/models/order.rb +8 -0
- data/test/models/playing_card.rb +4 -0
- data/test/models/promotion.rb +2 -0
- data/test/models/question.rb +2 -0
- data/test/models/rule.rb +2 -0
- data/test/models/tag.rb +3 -0
- data/test/models/tag_alias.rb +5 -0
- data/test/models/topic.rb +2 -0
- data/test/models/user.rb +2 -0
- data/test/models/user_token.rb +3 -0
- data/test/models/vendor.rb +2 -0
- data/test/models/widget.rb +2 -0
- data/test/mysql2/import_test.rb +2 -0
- data/test/mysql2_makara/import_test.rb +2 -0
- data/test/mysqlspatial2/import_test.rb +2 -0
- data/test/postgis/import_test.rb +2 -0
- data/test/postgresql/import_test.rb +2 -0
- data/test/schema/generic_schema.rb +33 -0
- data/test/schema/jdbcpostgresql_schema.rb +2 -0
- data/test/schema/mysql2_schema.rb +2 -0
- data/test/schema/postgis_schema.rb +2 -0
- data/test/schema/postgresql_schema.rb +18 -0
- data/test/schema/sqlite3_schema.rb +2 -0
- data/test/schema/version.rb +2 -0
- data/test/sqlite3/import_test.rb +2 -0
- data/test/support/active_support/test_case_extensions.rb +2 -0
- data/test/support/assertions.rb +2 -0
- data/test/support/factories.rb +2 -0
- data/test/support/generate.rb +4 -2
- data/test/support/mysql/import_examples.rb +2 -1
- data/test/support/postgresql/import_examples.rb +115 -2
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +2 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +32 -0
- data/test/support/shared_examples/recursive_import.rb +61 -1
- data/test/support/sqlite3/import_examples.rb +4 -16
- data/test/synchronize_test.rb +2 -0
- data/test/test_helper.rb +27 -2
- data/test/value_sets_bytes_parser_test.rb +2 -0
- data/test/value_sets_records_parser_test.rb +2 -0
- metadata +29 -16
- data/.travis.yml +0 -71
- data/gemfiles/3.2.gemfile +0 -2
- data/gemfiles/4.0.gemfile +0 -2
- data/gemfiles/4.1.gemfile +0 -2
data/benchmarks/lib/float.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'erb'
|
|
2
4
|
|
|
3
5
|
module OutputToHTML
|
|
4
|
-
TEMPLATE_HEADER = <<"EOT"
|
|
6
|
+
TEMPLATE_HEADER = <<"EOT"
|
|
5
7
|
<div>
|
|
6
8
|
All times are rounded to the nearest thousandth for display purposes. Speedups next to each time are computed
|
|
7
9
|
before any rounding occurs. Also, all speedup calculations are computed by comparing a given time against
|
|
@@ -9,7 +11,7 @@ module OutputToHTML
|
|
|
9
11
|
</div>
|
|
10
12
|
EOT
|
|
11
13
|
|
|
12
|
-
TEMPLATE = <<"EOT"
|
|
14
|
+
TEMPLATE = <<"EOT"
|
|
13
15
|
<style>
|
|
14
16
|
td#benchmarkTitle {
|
|
15
17
|
border: 1px solid black;
|
data/gemfiles/4.2.gemfile
CHANGED
data/gemfiles/5.0.gemfile
CHANGED
data/gemfiles/5.1.gemfile
CHANGED
data/gemfiles/5.2.gemfile
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
require "activerecord-import/adapters/mysql_adapter"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
require "active_record/connection_adapters/mysql2_adapter"
|
|
4
|
+
require "activerecord-import/adapters/mysql2_adapter"
|
|
5
|
+
|
|
6
|
+
class ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
|
7
|
+
include ActiveRecord::Import::Mysql2Adapter
|
|
6
8
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::AbstractAdapter
|
|
2
4
|
module InstanceMethods
|
|
3
5
|
def next_value_for_sequence(sequence_name)
|
|
@@ -46,7 +48,7 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
46
48
|
|
|
47
49
|
if supports_on_duplicate_key_update? && options[:on_duplicate_key_update]
|
|
48
50
|
post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update], options[:primary_key], options[:locking_column] )
|
|
49
|
-
elsif options[:on_duplicate_key_update]
|
|
51
|
+
elsif logger && options[:on_duplicate_key_update]
|
|
50
52
|
logger.warn "Ignoring on_duplicate_key_update because it is not supported by the database."
|
|
51
53
|
end
|
|
52
54
|
|
|
@@ -59,6 +61,12 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
59
61
|
post_sql_statements
|
|
60
62
|
end
|
|
61
63
|
|
|
64
|
+
def increment_locking_column!(table_name, results, locking_column)
|
|
65
|
+
if locking_column.present?
|
|
66
|
+
results << "\"#{locking_column}\"=#{table_name}.\"#{locking_column}\"+1"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
62
70
|
def supports_on_duplicate_key_update?
|
|
63
71
|
false
|
|
64
72
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::MysqlAdapter
|
|
2
4
|
include ActiveRecord::Import::ImportSupport
|
|
3
5
|
include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
|
|
@@ -56,9 +58,9 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
56
58
|
# in a single packet
|
|
57
59
|
def max_allowed_packet # :nodoc:
|
|
58
60
|
@max_allowed_packet ||= begin
|
|
59
|
-
result = execute( "
|
|
61
|
+
result = execute( "SELECT @@max_allowed_packet" )
|
|
60
62
|
# original Mysql gem responds to #fetch_row while Mysql2 responds to #first
|
|
61
|
-
val = result.respond_to?(:fetch_row) ? result.fetch_row[
|
|
63
|
+
val = result.respond_to?(:fetch_row) ? result.fetch_row[0] : result.first[0]
|
|
62
64
|
val.to_i
|
|
63
65
|
end
|
|
64
66
|
end
|
|
@@ -71,21 +73,18 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
71
73
|
|
|
72
74
|
# Add a column to be updated on duplicate key update
|
|
73
75
|
def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
|
|
74
|
-
if options
|
|
75
|
-
columns = options[:on_duplicate_key_update]
|
|
76
|
+
if (columns = options[:on_duplicate_key_update])
|
|
76
77
|
case columns
|
|
77
78
|
when Array then columns << column.to_sym unless columns.include?(column.to_sym)
|
|
78
79
|
when Hash then columns[column.to_sym] = column.to_sym
|
|
79
80
|
end
|
|
80
|
-
elsif !options[:ignore] && !options[:on_duplicate_key_ignore]
|
|
81
|
-
options[:on_duplicate_key_update] = [column.to_sym]
|
|
82
81
|
end
|
|
83
82
|
end
|
|
84
83
|
|
|
85
84
|
# Returns a generated ON DUPLICATE KEY UPDATE statement given the passed
|
|
86
85
|
# in +args+.
|
|
87
86
|
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
|
88
|
-
sql = ' ON DUPLICATE KEY UPDATE '
|
|
87
|
+
sql = ' ON DUPLICATE KEY UPDATE '.dup
|
|
89
88
|
arg = args.first
|
|
90
89
|
locking_column = args.last
|
|
91
90
|
if arg.is_a?( Array )
|
|
@@ -105,7 +104,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
105
104
|
qc = quote_column_name( column )
|
|
106
105
|
"#{table_name}.#{qc}=VALUES(#{qc})"
|
|
107
106
|
end
|
|
108
|
-
increment_locking_column!(
|
|
107
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
109
108
|
results.join( ',' )
|
|
110
109
|
end
|
|
111
110
|
|
|
@@ -115,7 +114,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
115
114
|
qc2 = quote_column_name( column2 )
|
|
116
115
|
"#{table_name}.#{qc1}=VALUES( #{qc2} )"
|
|
117
116
|
end
|
|
118
|
-
increment_locking_column!(
|
|
117
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
119
118
|
results.join( ',')
|
|
120
119
|
end
|
|
121
120
|
|
|
@@ -124,9 +123,9 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
124
123
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry')
|
|
125
124
|
end
|
|
126
125
|
|
|
127
|
-
def increment_locking_column!(
|
|
126
|
+
def increment_locking_column!(table_name, results, locking_column)
|
|
128
127
|
if locking_column.present?
|
|
129
|
-
results << "
|
|
128
|
+
results << "`#{locking_column}`=#{table_name}.`#{locking_column}`+1"
|
|
130
129
|
end
|
|
131
130
|
end
|
|
132
131
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::PostgreSQLAdapter
|
|
2
4
|
include ActiveRecord::Import::ImportSupport
|
|
3
5
|
include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
|
|
@@ -6,7 +8,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
6
8
|
|
|
7
9
|
def insert_many( sql, values, options = {}, *args ) # :nodoc:
|
|
8
10
|
number_of_inserts = 1
|
|
9
|
-
returned_values =
|
|
11
|
+
returned_values = {}
|
|
10
12
|
ids = []
|
|
11
13
|
results = []
|
|
12
14
|
|
|
@@ -18,47 +20,53 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
18
20
|
|
|
19
21
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
if
|
|
23
|
+
selections = returning_selections(options)
|
|
24
|
+
if selections.blank? || (options[:no_returning] && !options[:recursive])
|
|
23
25
|
insert( sql2insert, *args )
|
|
24
26
|
else
|
|
25
|
-
returned_values = if
|
|
27
|
+
returned_values = if selections.size > 1
|
|
26
28
|
# Select composite columns
|
|
27
|
-
|
|
29
|
+
db_result = select_all( sql2insert, *args )
|
|
30
|
+
{ values: db_result.rows, columns: db_result.columns }
|
|
28
31
|
else
|
|
29
|
-
select_values( sql2insert, *args )
|
|
32
|
+
{ values: select_values( sql2insert, *args ) }
|
|
30
33
|
end
|
|
31
|
-
|
|
34
|
+
clear_query_cache if query_cache_enabled
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
if options[:returning].blank?
|
|
35
|
-
ids = returned_values
|
|
38
|
+
ids = Array(returned_values[:values])
|
|
36
39
|
elsif options[:primary_key].blank?
|
|
37
|
-
|
|
40
|
+
options[:returning_columns] ||= returned_values[:columns]
|
|
41
|
+
results = Array(returned_values[:values])
|
|
38
42
|
else
|
|
39
43
|
# split primary key and returning columns
|
|
40
|
-
ids, results = split_ids_and_results(returned_values,
|
|
44
|
+
ids, results, options[:returning_columns] = split_ids_and_results(returned_values, options)
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
ActiveRecord::Import::Result.new([], number_of_inserts, ids, results)
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
def split_ids_and_results(
|
|
50
|
+
def split_ids_and_results( selections, options )
|
|
47
51
|
ids = []
|
|
48
|
-
|
|
52
|
+
returning_values = []
|
|
53
|
+
|
|
54
|
+
columns = Array(selections[:columns])
|
|
55
|
+
values = Array(selections[:values])
|
|
49
56
|
id_indexes = Array(options[:primary_key]).map { |key| columns.index(key) }
|
|
50
|
-
|
|
57
|
+
returning_columns = columns.reject.with_index { |_, index| id_indexes.include?(index) }
|
|
58
|
+
returning_indexes = returning_columns.map { |column| columns.index(column) }
|
|
51
59
|
|
|
52
60
|
values.each do |value|
|
|
53
61
|
value_array = Array(value)
|
|
54
|
-
ids << id_indexes.map { |
|
|
55
|
-
|
|
62
|
+
ids << id_indexes.map { |index| value_array[index] }
|
|
63
|
+
returning_values << returning_indexes.map { |index| value_array[index] }
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
ids.map!(&:first) if id_indexes.size == 1
|
|
59
|
-
|
|
67
|
+
returning_values.map!(&:first) if returning_columns.size == 1
|
|
60
68
|
|
|
61
|
-
[ids,
|
|
69
|
+
[ids, returning_values, returning_columns]
|
|
62
70
|
end
|
|
63
71
|
|
|
64
72
|
def next_value_for_sequence(sequence_name)
|
|
@@ -73,25 +81,30 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
73
81
|
if (options[:ignore] || options[:on_duplicate_key_ignore]) && !options[:on_duplicate_key_update] && !options[:recursive]
|
|
74
82
|
sql << sql_for_on_duplicate_key_ignore( table_name, options[:on_duplicate_key_ignore] )
|
|
75
83
|
end
|
|
76
|
-
elsif options[:on_duplicate_key_ignore] && !options[:on_duplicate_key_update]
|
|
84
|
+
elsif logger && options[:on_duplicate_key_ignore] && !options[:on_duplicate_key_update]
|
|
77
85
|
logger.warn "Ignoring on_duplicate_key_ignore because it is not supported by the database."
|
|
78
86
|
end
|
|
79
87
|
|
|
80
88
|
sql += super(table_name, options)
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
unless
|
|
84
|
-
sql << " RETURNING
|
|
90
|
+
selections = returning_selections(options)
|
|
91
|
+
unless selections.blank? || (options[:no_returning] && !options[:recursive])
|
|
92
|
+
sql << " RETURNING #{selections.join(', ')}"
|
|
85
93
|
end
|
|
86
94
|
|
|
87
95
|
sql
|
|
88
96
|
end
|
|
89
97
|
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
def returning_selections(options)
|
|
99
|
+
selections = []
|
|
100
|
+
column_names = Array(options[:model].column_names)
|
|
101
|
+
|
|
102
|
+
selections += Array(options[:primary_key]) if options[:primary_key].present?
|
|
103
|
+
selections += Array(options[:returning]) if options[:returning].present?
|
|
104
|
+
|
|
105
|
+
selections.map do |selection|
|
|
106
|
+
column_names.include?(selection.to_s) ? "\"#{selection}\"" : selection
|
|
107
|
+
end
|
|
95
108
|
end
|
|
96
109
|
|
|
97
110
|
# Add a column to be updated on duplicate key update
|
|
@@ -123,7 +136,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
123
136
|
arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
|
|
124
137
|
return unless arg.is_a?( Hash )
|
|
125
138
|
|
|
126
|
-
sql = ' ON CONFLICT '
|
|
139
|
+
sql = ' ON CONFLICT '.dup
|
|
127
140
|
conflict_target = sql_for_conflict_target( arg )
|
|
128
141
|
|
|
129
142
|
columns = arg.fetch( :columns, [] )
|
|
@@ -158,7 +171,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
158
171
|
qc = quote_column_name( column )
|
|
159
172
|
"#{qc}=EXCLUDED.#{qc}"
|
|
160
173
|
end
|
|
161
|
-
increment_locking_column!(results, locking_column)
|
|
174
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
162
175
|
results.join( ',' )
|
|
163
176
|
end
|
|
164
177
|
|
|
@@ -168,7 +181,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
168
181
|
qc2 = quote_column_name( column2 )
|
|
169
182
|
"#{qc1}=EXCLUDED.#{qc2}"
|
|
170
183
|
end
|
|
171
|
-
increment_locking_column!(results, locking_column)
|
|
184
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
172
185
|
results.join( ',' )
|
|
173
186
|
end
|
|
174
187
|
|
|
@@ -179,9 +192,9 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
179
192
|
if constraint_name.present?
|
|
180
193
|
"ON CONSTRAINT #{constraint_name} "
|
|
181
194
|
elsif conflict_target.present?
|
|
182
|
-
'('
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
sql = '(' + Array( conflict_target ).reject( &:blank? ).join( ', ' ) + ') '
|
|
196
|
+
sql += "WHERE #{index_predicate} " if index_predicate
|
|
197
|
+
sql
|
|
185
198
|
end
|
|
186
199
|
end
|
|
187
200
|
|
|
@@ -195,17 +208,15 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
195
208
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('duplicate key')
|
|
196
209
|
end
|
|
197
210
|
|
|
198
|
-
def supports_on_duplicate_key_update?
|
|
199
|
-
|
|
211
|
+
def supports_on_duplicate_key_update?
|
|
212
|
+
database_version >= MIN_VERSION_FOR_UPSERT
|
|
200
213
|
end
|
|
201
214
|
|
|
202
215
|
def supports_setting_primary_key_of_imported_objects?
|
|
203
216
|
true
|
|
204
217
|
end
|
|
205
218
|
|
|
206
|
-
def
|
|
207
|
-
|
|
208
|
-
results << "\"#{locking_column}\"=EXCLUDED.\"#{locking_column}\"+1"
|
|
209
|
-
end
|
|
219
|
+
def database_version
|
|
220
|
+
defined?(postgresql_version) ? postgresql_version : super
|
|
210
221
|
end
|
|
211
222
|
end
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::SQLite3Adapter
|
|
2
4
|
include ActiveRecord::Import::ImportSupport
|
|
3
5
|
include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
|
|
4
6
|
|
|
5
|
-
MIN_VERSION_FOR_IMPORT = "3.7.11"
|
|
6
|
-
MIN_VERSION_FOR_UPSERT = "3.24.0"
|
|
7
|
+
MIN_VERSION_FOR_IMPORT = "3.7.11"
|
|
8
|
+
MIN_VERSION_FOR_UPSERT = "3.24.0"
|
|
7
9
|
SQLITE_LIMIT_COMPOUND_SELECT = 500
|
|
8
10
|
|
|
9
11
|
# Override our conformance to ActiveRecord::Import::ImportSupport interface
|
|
10
12
|
# to ensure that we only support import in supported version of SQLite.
|
|
11
13
|
# Which INSERT statements with multiple value sets was introduced in 3.7.11.
|
|
12
|
-
def supports_import?
|
|
13
|
-
|
|
14
|
-
true
|
|
15
|
-
else
|
|
16
|
-
false
|
|
17
|
-
end
|
|
14
|
+
def supports_import?
|
|
15
|
+
database_version >= MIN_VERSION_FOR_IMPORT
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
def supports_on_duplicate_key_update?
|
|
21
|
-
|
|
18
|
+
def supports_on_duplicate_key_update?
|
|
19
|
+
database_version >= MIN_VERSION_FOR_UPSERT
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
# +sql+ can be a single string or an array. If it is an array all
|
|
@@ -96,12 +94,12 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
96
94
|
|
|
97
95
|
# Returns a generated ON CONFLICT DO UPDATE statement given the passed
|
|
98
96
|
# in +args+.
|
|
99
|
-
def sql_for_on_duplicate_key_update(
|
|
97
|
+
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
|
100
98
|
arg, primary_key, locking_column = args
|
|
101
99
|
arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
|
|
102
100
|
return unless arg.is_a?( Hash )
|
|
103
101
|
|
|
104
|
-
sql = ' ON CONFLICT '
|
|
102
|
+
sql = ' ON CONFLICT '.dup
|
|
105
103
|
conflict_target = sql_for_conflict_target( arg )
|
|
106
104
|
|
|
107
105
|
columns = arg.fetch( :columns, [] )
|
|
@@ -117,9 +115,9 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
117
115
|
|
|
118
116
|
sql << "#{conflict_target}DO UPDATE SET "
|
|
119
117
|
if columns.is_a?( Array )
|
|
120
|
-
sql << sql_for_on_duplicate_key_update_as_array( locking_column, columns )
|
|
118
|
+
sql << sql_for_on_duplicate_key_update_as_array( table_name, locking_column, columns )
|
|
121
119
|
elsif columns.is_a?( Hash )
|
|
122
|
-
sql << sql_for_on_duplicate_key_update_as_hash( locking_column, columns )
|
|
120
|
+
sql << sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, columns )
|
|
123
121
|
elsif columns.is_a?( String )
|
|
124
122
|
sql << columns
|
|
125
123
|
else
|
|
@@ -131,22 +129,22 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
131
129
|
sql
|
|
132
130
|
end
|
|
133
131
|
|
|
134
|
-
def sql_for_on_duplicate_key_update_as_array( locking_column, arr ) # :nodoc:
|
|
132
|
+
def sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arr ) # :nodoc:
|
|
135
133
|
results = arr.map do |column|
|
|
136
134
|
qc = quote_column_name( column )
|
|
137
135
|
"#{qc}=EXCLUDED.#{qc}"
|
|
138
136
|
end
|
|
139
|
-
increment_locking_column!(results, locking_column)
|
|
137
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
140
138
|
results.join( ',' )
|
|
141
139
|
end
|
|
142
140
|
|
|
143
|
-
def sql_for_on_duplicate_key_update_as_hash( locking_column, hsh ) # :nodoc:
|
|
141
|
+
def sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, hsh ) # :nodoc:
|
|
144
142
|
results = hsh.map do |column1, column2|
|
|
145
143
|
qc1 = quote_column_name( column1 )
|
|
146
144
|
qc2 = quote_column_name( column2 )
|
|
147
145
|
"#{qc1}=EXCLUDED.#{qc2}"
|
|
148
146
|
end
|
|
149
|
-
increment_locking_column!(results, locking_column)
|
|
147
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
150
148
|
results.join( ',' )
|
|
151
149
|
end
|
|
152
150
|
|
|
@@ -154,9 +152,9 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
154
152
|
conflict_target = args[:conflict_target]
|
|
155
153
|
index_predicate = args[:index_predicate]
|
|
156
154
|
if conflict_target.present?
|
|
157
|
-
'('
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
sql = '(' + Array( conflict_target ).reject( &:blank? ).join( ', ' ) + ') '
|
|
156
|
+
sql += "WHERE #{index_predicate} " if index_predicate
|
|
157
|
+
sql
|
|
160
158
|
end
|
|
161
159
|
end
|
|
162
160
|
|
|
@@ -170,9 +168,7 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
170
168
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('duplicate key')
|
|
171
169
|
end
|
|
172
170
|
|
|
173
|
-
def
|
|
174
|
-
|
|
175
|
-
results << "\"#{locking_column}\"=EXCLUDED.\"#{locking_column}\"+1"
|
|
176
|
-
end
|
|
171
|
+
def database_version
|
|
172
|
+
defined?(sqlite_version) ? sqlite_version : super
|
|
177
173
|
end
|
|
178
174
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "pathname"
|
|
2
4
|
require "active_record"
|
|
3
5
|
require "active_record/version"
|
|
4
6
|
|
|
5
7
|
module ActiveRecord::Import
|
|
6
|
-
ADAPTER_PATH = "activerecord-import/active_record/adapters"
|
|
8
|
+
ADAPTER_PATH = "activerecord-import/active_record/adapters"
|
|
7
9
|
|
|
8
10
|
def self.base_adapter(adapter)
|
|
9
11
|
case adapter
|
|
@@ -13,6 +15,7 @@ module ActiveRecord::Import
|
|
|
13
15
|
when 'postgresql_makara' then 'postgresql'
|
|
14
16
|
when 'makara_postgis' then 'postgresql'
|
|
15
17
|
when 'postgis' then 'postgresql'
|
|
18
|
+
when 'cockroachdb' then 'postgresql'
|
|
16
19
|
else adapter
|
|
17
20
|
end
|
|
18
21
|
end
|
|
@@ -26,7 +29,13 @@ module ActiveRecord::Import
|
|
|
26
29
|
|
|
27
30
|
# Loads the import functionality for the passed in ActiveRecord connection
|
|
28
31
|
def self.load_from_connection_pool(connection_pool)
|
|
29
|
-
|
|
32
|
+
adapter =
|
|
33
|
+
if connection_pool.respond_to?(:db_config) # ActiveRecord >= 6.1
|
|
34
|
+
connection_pool.db_config.adapter
|
|
35
|
+
else
|
|
36
|
+
connection_pool.spec.config[:adapter]
|
|
37
|
+
end
|
|
38
|
+
require_adapter adapter
|
|
30
39
|
end
|
|
31
40
|
end
|
|
32
41
|
|