activerecord-import 1.0.3 → 1.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 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