activerecord-import 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9bf4dc4ecf93b15a493cc34134158d7df3d6391b2e6c7fafbe79b630df48ea3
4
- data.tar.gz: ab5f7f4707598308dc9a2e7d4be5816f1d5674f63cea58e48b29f6ed87fa059a
3
+ metadata.gz: cbf4c01d6a3f043ed541f7331d6a19e651cbc035fcc3030b17f1e741a6d4e8b7
4
+ data.tar.gz: a740491de16c78d4d94ccb428c69342fd8a809f235ce9d42fc41237a4d15c7e6
5
5
  SHA512:
6
- metadata.gz: 05c4ad6281d6f0f574ee30cffed790763fb393fdf906ecc20bb8b4504f640ef9734ea1a43284f9821e543f8900aa3023bf66bcf455afcb2419e15828da618a05
7
- data.tar.gz: cdc67be0552c3b32a980598a784964df98549a27d428b89ef3ed7385acb4c75681946e010e714770ac81ff076088c0293e26bc04bbc2e15aecd42bf86c3aecc8
6
+ metadata.gz: 48e62637f6493cd5446cd78aee3db3b4a58e646fb0bc07d3583a662f89f52b03c2a079c83fc2cae807c5577f922e1e3219354cf36dff8ae8cc81ae68266e711f
7
+ data.tar.gz: 2f40f5fd61975589f8af932a3dbf5b6d14d2cfc6884ebe7019c85956c8c380d3faebffb1b600a7fa04536fe262ed40688946c8d35b8cf5b1bfedd1ddff20af5a
@@ -1,3 +1,12 @@
1
+ ## Changes in 1.0.4
2
+
3
+ ### Fixes
4
+
5
+ * Use prepend pattern for ActiveRecord::Base#establish_connection patching. Thanks to @dombesz via \#648.
6
+ * Fix NoMethodError when using PostgreSQL ENUM types. Thanks to @sebcoetzee via \#651.
7
+ * Fix issue updating optimistic lock in Postgres. Thanks to @timanovsky
8
+ via \#656.
9
+
1
10
  ## Changes in 1.0.3
2
11
 
3
12
  ### New Features
@@ -251,6 +251,7 @@ Key | Options | Default | Descripti
251
251
  ----------------------- | --------------------- | ------------------ | -----------
252
252
  :validate | `true`/`false` | `true` | Whether or not to run `ActiveRecord` validations (uniqueness skipped). This option will always be true when using `import!`.
253
253
  :validate_uniqueness | `true`/`false` | `false` | Whether or not to run uniqueness validations, has potential pitfalls, use with caution (requires `>= v0.27.0`).
254
+ :validate_with_context | `Symbol` |`:create`/`:update` | Allows passing an ActiveModel validation context for each model. Default is `:create` for new records and `:update` for existing ones.
254
255
  :on_duplicate_key_ignore| `true`/`false` | `false` | Allows skipping records with duplicate keys. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-ignore) for more details.
255
256
  :ignore | `true`/`false` | `false` | Alias for :on_duplicate_key_ignore.
256
257
  :on_duplicate_key_update| :all, `Array`, `Hash` | N/A | Allows upsert logic to be used. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-update) for more details.
@@ -258,8 +259,8 @@ Key | Options | Default | Descripti
258
259
  :timestamps | `true`/`false` | `true` | Enables/disables timestamps on imported records.
259
260
  :recursive | `true`/`false` | `false` | Imports has_many/has_one associations (PostgreSQL only).
260
261
  :batch_size | `Integer` | total # of records | Max number of records to insert per import
261
- :raise_error | `true`/`false` | `false` | Throws an exception if there are invalid records. `import!` is a shortcut for this.
262
-
262
+ :raise_error | `true`/`false` | `false` | Raises an exception at the first invalid record. This means there will not be a result object returned. The `import!` method is a shortcut for this.
263
+ :all_or_none | `true`/`false` | `false` | Will not import any records if there is a record with validation errors.
263
264
 
264
265
  #### Duplicate Key Ignore
265
266
 
@@ -59,8 +59,14 @@ module ActiveRecord::Import::AbstractAdapter
59
59
  post_sql_statements
60
60
  end
61
61
 
62
+ def increment_locking_column!(table_name, results, locking_column)
63
+ if locking_column.present?
64
+ results << "\"#{locking_column}\"=#{table_name}.\"#{locking_column}\"+1"
65
+ end
66
+ end
67
+
62
68
  def supports_on_duplicate_key_update?
63
- false
69
+ true
64
70
  end
65
71
  end
66
72
  end
@@ -1,6 +1,5 @@
1
1
  module ActiveRecord::Import::MysqlAdapter
2
2
  include ActiveRecord::Import::ImportSupport
3
- include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
4
3
 
5
4
  NO_MAX_PACKET = 0
6
5
  QUERY_OVERHEAD = 8 # This was shown to be true for MySQL, but it's not clear where the overhead is from.
@@ -102,7 +101,7 @@ module ActiveRecord::Import::MysqlAdapter
102
101
  qc = quote_column_name( column )
103
102
  "#{table_name}.#{qc}=VALUES(#{qc})"
104
103
  end
105
- increment_locking_column!(results, table_name, locking_column)
104
+ increment_locking_column!(table_name, results, locking_column)
106
105
  results.join( ',' )
107
106
  end
108
107
 
@@ -112,7 +111,7 @@ module ActiveRecord::Import::MysqlAdapter
112
111
  qc2 = quote_column_name( column2 )
113
112
  "#{table_name}.#{qc1}=VALUES( #{qc2} )"
114
113
  end
115
- increment_locking_column!(results, table_name, locking_column)
114
+ increment_locking_column!(table_name, results, locking_column)
116
115
  results.join( ',')
117
116
  end
118
117
 
@@ -121,9 +120,9 @@ module ActiveRecord::Import::MysqlAdapter
121
120
  exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry')
122
121
  end
123
122
 
124
- def increment_locking_column!(results, table_name, locking_column)
123
+ def increment_locking_column!(table_name, results, locking_column)
125
124
  if locking_column.present?
126
- results << "#{table_name}.`#{locking_column}`=`#{locking_column}`+1"
125
+ results << "`#{locking_column}`=#{table_name}.`#{locking_column}`+1"
127
126
  end
128
127
  end
129
128
  end
@@ -1,6 +1,5 @@
1
1
  module ActiveRecord::Import::PostgreSQLAdapter
2
2
  include ActiveRecord::Import::ImportSupport
3
- include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
4
3
 
5
4
  MIN_VERSION_FOR_UPSERT = 90_500
6
5
 
@@ -158,7 +157,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
158
157
  qc = quote_column_name( column )
159
158
  "#{qc}=EXCLUDED.#{qc}"
160
159
  end
161
- increment_locking_column!(results, locking_column)
160
+ increment_locking_column!(table_name, results, locking_column)
162
161
  results.join( ',' )
163
162
  end
164
163
 
@@ -168,7 +167,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
168
167
  qc2 = quote_column_name( column2 )
169
168
  "#{qc1}=EXCLUDED.#{qc2}"
170
169
  end
171
- increment_locking_column!(results, locking_column)
170
+ increment_locking_column!(table_name, results, locking_column)
172
171
  results.join( ',' )
173
172
  end
174
173
 
@@ -203,12 +202,6 @@ module ActiveRecord::Import::PostgreSQLAdapter
203
202
  true
204
203
  end
205
204
 
206
- def increment_locking_column!(results, locking_column)
207
- if locking_column.present?
208
- results << "\"#{locking_column}\"=EXCLUDED.\"#{locking_column}\"+1"
209
- end
210
- end
211
-
212
205
  private
213
206
 
214
207
  def database_version
@@ -1,6 +1,5 @@
1
1
  module ActiveRecord::Import::SQLite3Adapter
2
2
  include ActiveRecord::Import::ImportSupport
3
- include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
4
3
 
5
4
  MIN_VERSION_FOR_IMPORT = "3.7.11".freeze
6
5
  MIN_VERSION_FOR_UPSERT = "3.24.0".freeze
@@ -92,7 +91,7 @@ module ActiveRecord::Import::SQLite3Adapter
92
91
 
93
92
  # Returns a generated ON CONFLICT DO UPDATE statement given the passed
94
93
  # in +args+.
95
- def sql_for_on_duplicate_key_update( _table_name, *args ) # :nodoc:
94
+ def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
96
95
  arg, primary_key, locking_column = args
97
96
  arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
98
97
  return unless arg.is_a?( Hash )
@@ -113,9 +112,9 @@ module ActiveRecord::Import::SQLite3Adapter
113
112
 
114
113
  sql << "#{conflict_target}DO UPDATE SET "
115
114
  if columns.is_a?( Array )
116
- sql << sql_for_on_duplicate_key_update_as_array( locking_column, columns )
115
+ sql << sql_for_on_duplicate_key_update_as_array( table_name, locking_column, columns )
117
116
  elsif columns.is_a?( Hash )
118
- sql << sql_for_on_duplicate_key_update_as_hash( locking_column, columns )
117
+ sql << sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, columns )
119
118
  elsif columns.is_a?( String )
120
119
  sql << columns
121
120
  else
@@ -127,22 +126,22 @@ module ActiveRecord::Import::SQLite3Adapter
127
126
  sql
128
127
  end
129
128
 
130
- def sql_for_on_duplicate_key_update_as_array( locking_column, arr ) # :nodoc:
129
+ def sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arr ) # :nodoc:
131
130
  results = arr.map do |column|
132
131
  qc = quote_column_name( column )
133
132
  "#{qc}=EXCLUDED.#{qc}"
134
133
  end
135
- increment_locking_column!(results, locking_column)
134
+ increment_locking_column!(table_name, results, locking_column)
136
135
  results.join( ',' )
137
136
  end
138
137
 
139
- def sql_for_on_duplicate_key_update_as_hash( locking_column, hsh ) # :nodoc:
138
+ def sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, hsh ) # :nodoc:
140
139
  results = hsh.map do |column1, column2|
141
140
  qc1 = quote_column_name( column1 )
142
141
  qc2 = quote_column_name( column2 )
143
142
  "#{qc1}=EXCLUDED.#{qc2}"
144
143
  end
145
- increment_locking_column!(results, locking_column)
144
+ increment_locking_column!(table_name, results, locking_column)
146
145
  results.join( ',' )
147
146
  end
148
147
 
@@ -166,12 +165,6 @@ module ActiveRecord::Import::SQLite3Adapter
166
165
  exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('duplicate key')
167
166
  end
168
167
 
169
- def increment_locking_column!(results, locking_column)
170
- if locking_column.present?
171
- results << "\"#{locking_column}\"=EXCLUDED.\"#{locking_column}\"+1"
172
- end
173
- end
174
-
175
168
  private
176
169
 
177
170
  def database_version
@@ -11,12 +11,6 @@ module ActiveRecord::Import #:nodoc:
11
11
  end
12
12
  end
13
13
 
14
- module OnDuplicateKeyUpdateSupport #:nodoc:
15
- def supports_on_duplicate_key_update? #:nodoc:
16
- true
17
- end
18
- end
19
-
20
14
  class MissingColumnError < StandardError
21
15
  def initialize(name, index)
22
16
  super "Missing column for value <#{name}> at index #{index}"
@@ -245,16 +239,16 @@ class ActiveRecord::Associations::CollectionAssociation
245
239
  alias import bulk_import unless respond_to? :import
246
240
  end
247
241
 
242
+ module ActiveRecord::Import::Connection
243
+ def establish_connection(args = nil)
244
+ super(args)
245
+ ActiveRecord::Import.load_from_connection_pool connection_pool
246
+ end
247
+ end
248
+
248
249
  class ActiveRecord::Base
249
250
  class << self
250
- def establish_connection_with_activerecord_import(*args)
251
- conn = establish_connection_without_activerecord_import(*args)
252
- ActiveRecord::Import.load_from_connection_pool connection_pool
253
- conn
254
- end
255
-
256
- alias establish_connection_without_activerecord_import establish_connection
257
- alias establish_connection establish_connection_with_activerecord_import
251
+ prepend ActiveRecord::Import::Connection
258
252
 
259
253
  # Returns true if the current database connection adapter
260
254
  # supports import functionality, otherwise returns false.
@@ -964,7 +958,7 @@ class ActiveRecord::Base
964
958
  val = serialized_attributes[column.name].dump(val)
965
959
  end
966
960
  # Fixes #443 to support binary (i.e. bytea) columns on PG
967
- val = column.type_cast(val) unless column.type.to_sym == :binary
961
+ val = column.type_cast(val) unless column.type && column.type.to_sym == :binary
968
962
  connection_memo.quote(val, column)
969
963
  end
970
964
  else
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "1.0.3".freeze
3
+ VERSION = "1.0.4".freeze
4
4
  end
5
5
  end
@@ -3,6 +3,17 @@ ActiveRecord::Schema.define do
3
3
  execute('CREATE extension IF NOT EXISTS "pgcrypto";')
4
4
  execute('CREATE extension IF NOT EXISTS "uuid-ossp";')
5
5
 
6
+ # create ENUM if it does not exist yet
7
+ begin
8
+ execute('CREATE TYPE vendor_type AS ENUM (\'wholesaler\', \'retailer\');')
9
+ rescue ActiveRecord::StatementInvalid => e
10
+ # since PostgreSQL does not support IF NOT EXISTS when creating a TYPE,
11
+ # rescue the error and check the error class
12
+ raise unless e.cause.is_a? PG::DuplicateObject
13
+ execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'wholesaler\';')
14
+ execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'retailer\';')
15
+ end
16
+
6
17
  create_table :vendors, id: :uuid, force: :cascade do |t|
7
18
  t.string :name, null: true
8
19
  t.text :preferences
@@ -29,6 +40,8 @@ ActiveRecord::Schema.define do
29
40
  t.text :json_data
30
41
  end
31
42
 
43
+ t.column :vendor_type, :vendor_type
44
+
32
45
  t.datetime :created_at
33
46
  t.datetime :updated_at
34
47
  end
@@ -260,6 +260,17 @@ def should_support_postgresql_import_functionality
260
260
  end
261
261
  end
262
262
 
263
+ describe "with enum field" do
264
+ let(:vendor_type) { "retailer" }
265
+ it "imports the correct values for enum fields" do
266
+ vendor = Vendor.new(name: 'Vendor 1', vendor_type: vendor_type)
267
+ assert_difference "Vendor.count", +1 do
268
+ Vendor.import [vendor]
269
+ end
270
+ assert_equal(vendor_type, Vendor.first.vendor_type)
271
+ end
272
+ end
273
+
263
274
  describe "with binary field" do
264
275
  let(:binary_value) { "\xE0'c\xB2\xB0\xB3Bh\\\xC2M\xB1m\\I\xC4r".force_encoding('ASCII-8BIT') }
265
276
  it "imports the correct values for binary fields" do
@@ -73,6 +73,16 @@ def should_support_basic_on_duplicate_key_update
73
73
  assert_equal user.name, users[i].name + ' Rothschild'
74
74
  assert_equal 1, user.lock_version
75
75
  end
76
+ updated_values2 = User.all.map do |user|
77
+ user.name += ' jr.'
78
+ { id: user.id, name: user.name }
79
+ end
80
+ User.import(updated_values2, on_duplicate_key_update: [:name])
81
+ assert User.count == updated_values2.length
82
+ User.all.each_with_index do |user, i|
83
+ assert_equal user.name, users[i].name + ' Rothschild jr.'
84
+ assert_equal 2, user.lock_version
85
+ end
76
86
  end
77
87
 
78
88
  it 'upsert optimistic lock columns other than lock_version by model' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-import
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Dennis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-09 00:00:00.000000000 Z
11
+ date: 2019-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -185,8 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
185
  - !ruby/object:Gem::Version
186
186
  version: '0'
187
187
  requirements: []
188
- rubyforge_project:
189
- rubygems_version: 2.7.7
188
+ rubygems_version: 3.0.6
190
189
  signing_key:
191
190
  specification_version: 4
192
191
  summary: Bulk insert extension for ActiveRecord