activerecord-updateinbulk 0.2.0 → 0.2.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 +4 -4
- data/lib/activerecord-updateinbulk/adapters/postgresql_adapter.rb +2 -1
- data/lib/activerecord-updateinbulk/base.rb +12 -0
- data/lib/activerecord-updateinbulk/builder.rb +52 -44
- data/lib/activerecord-updateinbulk/railtie.rb +14 -10
- data/lib/activerecord-updateinbulk/version.rb +1 -1
- metadata +1 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a844813b98d092d304542eb4f8c3ffc6b99a50f7510d430bef229e47c969415
|
|
4
|
+
data.tar.gz: e83aa71485ad9255b977b96afe9a8ec55820eda485d489d16e30629c1a79a971
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 45d04870ea3981cc5bb3533572f8fd412cc442bfbde77163d2d78bb47af3e3234658db990afdea09fc754f6d492ed400bc50f7f23508f01a82614f1803fae05e
|
|
7
|
+
data.tar.gz: 33dfb33a7963b357eb8da64afc3353e671fa33cab926e8bf3b9380ccecd47dbae1360f776159e35dc02a44c0c8f98721d7ed58306eebc456bf89673b0fc14900
|
|
@@ -12,8 +12,9 @@ module ActiveRecord::UpdateInBulk
|
|
|
12
12
|
case column
|
|
13
13
|
when ActiveRecord::ConnectionAdapters::PostgreSQL::Column
|
|
14
14
|
if SAFE_TYPES_FOR_VALUES_TABLE.exclude?(column.type) ||
|
|
15
|
+
column.array ||
|
|
15
16
|
values_table.rows.all? { |row| row[index].nil? }
|
|
16
|
-
column.sql_type
|
|
17
|
+
column.sql_type_metadata.sql_type
|
|
17
18
|
end
|
|
18
19
|
when Arel::Nodes::SqlLiteral, nil
|
|
19
20
|
column
|
|
@@ -18,6 +18,18 @@ module ActiveRecord
|
|
|
18
18
|
def self.load_from_connection_pool(connection_pool) # :nodoc:
|
|
19
19
|
require_adapter connection_pool.db_config.adapter
|
|
20
20
|
end
|
|
21
|
+
|
|
22
|
+
def self.register_formula(name, &formula)
|
|
23
|
+
Builder.register_formula(name, &formula)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.unregister_formula(name)
|
|
27
|
+
Builder.unregister_formula(name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.registered_formula?(name)
|
|
31
|
+
Builder.registered_formula?(name)
|
|
32
|
+
end
|
|
21
33
|
end
|
|
22
34
|
end
|
|
23
35
|
|
|
@@ -4,13 +4,31 @@ require "active_support/core_ext/enumerable"
|
|
|
4
4
|
|
|
5
5
|
module ActiveRecord::UpdateInBulk
|
|
6
6
|
class Builder # :nodoc:
|
|
7
|
-
FORMULAS = [:add, :subtract, :concat_append, :concat_prepend].freeze
|
|
8
7
|
SAFE_COMPARISON_TYPES = [:boolean, :string, :text, :integer, :float, :decimal].freeze
|
|
9
8
|
|
|
10
9
|
class << self
|
|
11
10
|
attr_accessor :values_table_name
|
|
12
11
|
attr_accessor :ignore_scope_order
|
|
13
12
|
|
|
13
|
+
def register_formula(name, &formula)
|
|
14
|
+
raise ArgumentError, "Missing block" unless formula
|
|
15
|
+
|
|
16
|
+
name = name.to_sym
|
|
17
|
+
if registered_formulas.key?(name)
|
|
18
|
+
raise ArgumentError, "Formula already registered: #{name.inspect}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
registered_formulas[name] = formula
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def unregister_formula(name)
|
|
25
|
+
registered_formulas.delete(name.to_sym)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def registered_formula?(name)
|
|
29
|
+
registered_formulas.key?(name.to_sym)
|
|
30
|
+
end
|
|
31
|
+
|
|
14
32
|
# Normalize all input formats into separated format [conditions, assigns].
|
|
15
33
|
def normalize_updates(model, updates, values = nil)
|
|
16
34
|
conditions = []
|
|
@@ -48,39 +66,37 @@ module ActiveRecord::UpdateInBulk
|
|
|
48
66
|
end
|
|
49
67
|
|
|
50
68
|
def apply_formula(formula, lhs, rhs, model)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
when :add
|
|
54
|
-
lhs + rhs
|
|
55
|
-
when :subtract
|
|
56
|
-
lhs - rhs
|
|
57
|
-
when :concat_append
|
|
58
|
-
lhs.concat(rhs)
|
|
59
|
-
when :concat_prepend
|
|
60
|
-
rhs.concat(lhs)
|
|
61
|
-
when Proc
|
|
62
|
-
node = apply_proc_formula(formula, lhs, rhs, model)
|
|
63
|
-
unless Arel.arel_node?(node)
|
|
64
|
-
raise ArgumentError, "Custom formula must return an Arel node"
|
|
65
|
-
end
|
|
66
|
-
node
|
|
69
|
+
formula_proc = case formula
|
|
70
|
+
when Proc then formula
|
|
67
71
|
else
|
|
68
|
-
|
|
72
|
+
registered_formulas.fetch(formula.to_sym) do
|
|
73
|
+
raise ArgumentError, "Unknown formula: #{formula.inspect}"
|
|
74
|
+
end
|
|
69
75
|
end
|
|
76
|
+
|
|
77
|
+
node = apply_proc_formula(formula_proc, lhs, rhs, model)
|
|
78
|
+
raise ArgumentError, "Custom formula must return an Arel node" unless Arel.arel_node?(node)
|
|
79
|
+
|
|
80
|
+
node
|
|
70
81
|
end
|
|
71
82
|
|
|
72
83
|
def apply_proc_formula(formula, lhs, rhs, model)
|
|
73
84
|
case formula.arity
|
|
74
|
-
when 2
|
|
75
|
-
|
|
76
|
-
when 3
|
|
77
|
-
formula.call(lhs, rhs, model)
|
|
78
|
-
else
|
|
79
|
-
raise ArgumentError, "Custom formula must accept 2 or 3 arguments"
|
|
85
|
+
when 2 then formula.call(lhs, rhs)
|
|
86
|
+
else formula.call(lhs, rhs, model)
|
|
80
87
|
end
|
|
81
88
|
end
|
|
82
89
|
|
|
83
90
|
private
|
|
91
|
+
def registered_formulas
|
|
92
|
+
@registered_formulas ||= {
|
|
93
|
+
add: lambda { |lhs, rhs| lhs + rhs },
|
|
94
|
+
subtract: lambda { |lhs, rhs| lhs - rhs },
|
|
95
|
+
concat_append: lambda { |lhs, rhs| lhs.concat(rhs) },
|
|
96
|
+
concat_prepend: lambda { |lhs, rhs| rhs.concat(lhs) }
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
84
100
|
def normalize_conditions(model, conditions)
|
|
85
101
|
if conditions.is_a?(Hash)
|
|
86
102
|
conditions
|
|
@@ -126,10 +142,8 @@ module ActiveRecord::UpdateInBulk
|
|
|
126
142
|
resolve_attribute_aliases!
|
|
127
143
|
resolve_read_and_write_keys!
|
|
128
144
|
verify_read_and_write_keys!
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
serialize_values!
|
|
132
|
-
end
|
|
145
|
+
serialize_values!
|
|
146
|
+
detect_constant_columns! unless simple_update?
|
|
133
147
|
end
|
|
134
148
|
|
|
135
149
|
def build_arel
|
|
@@ -160,26 +174,24 @@ module ActiveRecord::UpdateInBulk
|
|
|
160
174
|
def build_simple_conditions(table)
|
|
161
175
|
row_conditions = @conditions.first
|
|
162
176
|
read_keys.map do |key|
|
|
163
|
-
table[key].eq(
|
|
177
|
+
table[key].eq(quoted_value(row_conditions.fetch(key)))
|
|
164
178
|
end
|
|
165
179
|
end
|
|
166
180
|
|
|
167
181
|
def build_simple_assignments(table)
|
|
168
182
|
row_assigns = @assigns.first
|
|
169
183
|
write_keys.map do |key|
|
|
170
|
-
[table[key],
|
|
184
|
+
[table[key], quoted_value(row_assigns.fetch(key))]
|
|
171
185
|
end
|
|
172
186
|
end
|
|
173
187
|
|
|
174
188
|
def detect_constant_columns!
|
|
175
189
|
@constant_assigns = {}
|
|
176
|
-
columns_hash = model.columns_hash
|
|
177
190
|
|
|
178
191
|
(write_keys - optional_keys).each do |key|
|
|
179
192
|
next if @formulas.key?(key) # need to pass Arel::Attribute as argument to formula
|
|
180
|
-
next unless SAFE_COMPARISON_TYPES.include?(columns_hash.fetch(key).type)
|
|
181
193
|
first = @assigns.first[key]
|
|
182
|
-
@constant_assigns[key] = first if @assigns.all? { |a| !
|
|
194
|
+
@constant_assigns[key] = first if @assigns.all? { |a| !Arel.arel_node?(v = a[key]) && v == first }
|
|
183
195
|
end
|
|
184
196
|
end
|
|
185
197
|
|
|
@@ -187,14 +199,14 @@ module ActiveRecord::UpdateInBulk
|
|
|
187
199
|
types = read_keys.index_with { |key| model.type_for_attribute(key) }
|
|
188
200
|
@conditions.each do |row|
|
|
189
201
|
row.each do |key, value|
|
|
190
|
-
next if
|
|
202
|
+
next if Arel.arel_node?(value)
|
|
191
203
|
row[key] = ActiveModel::Type::SerializeCastValue.serialize(type = types[key], type.cast(value))
|
|
192
204
|
end
|
|
193
205
|
end
|
|
194
206
|
types = write_keys.index_with { |key| model.type_for_attribute(key) }
|
|
195
207
|
@assigns.each do |row|
|
|
196
208
|
row.each do |key, value|
|
|
197
|
-
next if
|
|
209
|
+
next if Arel.arel_node?(value)
|
|
198
210
|
row[key] = ActiveModel::Type::SerializeCastValue.serialize(type = types[key], type.cast(value))
|
|
199
211
|
end
|
|
200
212
|
end
|
|
@@ -274,7 +286,7 @@ module ActiveRecord::UpdateInBulk
|
|
|
274
286
|
lhs = table[key]
|
|
275
287
|
|
|
276
288
|
if constant_assigns.key?(key)
|
|
277
|
-
rhs = Arel::Nodes::
|
|
289
|
+
rhs = Arel::Nodes::Quoted.new(constant_assigns[key])
|
|
278
290
|
else
|
|
279
291
|
rhs = values_table[column]
|
|
280
292
|
column += 1
|
|
@@ -327,15 +339,11 @@ module ActiveRecord::UpdateInBulk
|
|
|
327
339
|
# When you assign a value to NULL, we need to use a bitmask to distinguish that
|
|
328
340
|
# row in the values table from rows where the column is not to be assigned at all.
|
|
329
341
|
def might_be_nil_value?(value)
|
|
330
|
-
value.nil? ||
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
def opaque_value?(value)
|
|
334
|
-
Arel.arel_node?(value)
|
|
342
|
+
value.nil? || Arel.arel_node?(value)
|
|
335
343
|
end
|
|
336
344
|
|
|
337
|
-
def
|
|
338
|
-
|
|
345
|
+
def quoted_value(value)
|
|
346
|
+
Arel.arel_node?(value) ? value : Arel::Nodes::Quoted.new(value)
|
|
339
347
|
end
|
|
340
348
|
|
|
341
349
|
def normalize_formulas(formulas)
|
|
@@ -344,7 +352,7 @@ module ActiveRecord::UpdateInBulk
|
|
|
344
352
|
normalized = formulas.to_h do |key, value|
|
|
345
353
|
[key.to_s, value.is_a?(Proc) ? value : value.to_sym]
|
|
346
354
|
end
|
|
347
|
-
invalid = normalized.values.reject { |v| v.is_a?(Proc)
|
|
355
|
+
invalid = normalized.values.reject { |v| v.is_a?(Proc) || self.class.registered_formula?(v) }
|
|
348
356
|
if invalid.any?
|
|
349
357
|
raise ArgumentError, "Unknown formula: #{invalid.first.inspect}"
|
|
350
358
|
end
|
|
@@ -29,21 +29,25 @@ module ActiveRecord
|
|
|
29
29
|
config.active_record_update_in_bulk = ActiveSupport::OrderedOptions.new
|
|
30
30
|
config.active_record_update_in_bulk.ignore_scope_order = true
|
|
31
31
|
|
|
32
|
-
initializer "active_record_update_in_bulk.values_table_alias"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
initializer "active_record_update_in_bulk.values_table_alias" do |app|
|
|
33
|
+
ActiveSupport.on_load(:active_record) do
|
|
34
|
+
if (bulk_alias = app.config.active_record_update_in_bulk.values_table_alias)
|
|
35
|
+
unless bulk_alias.instance_of?(String) && !bulk_alias.empty?
|
|
36
|
+
raise ArgumentError, "values_table_alias must be a non-empty String"
|
|
37
|
+
end
|
|
38
|
+
ActiveRecord::UpdateInBulk::Builder.values_table_name = bulk_alias
|
|
36
39
|
end
|
|
37
|
-
ActiveRecord::UpdateInBulk::Builder.values_table_name = bulk_alias
|
|
38
40
|
end
|
|
39
41
|
end
|
|
40
42
|
|
|
41
|
-
initializer "active_record_update_in_bulk.ignore_scope_order"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
initializer "active_record_update_in_bulk.ignore_scope_order" do |app|
|
|
44
|
+
ActiveSupport.on_load(:active_record) do
|
|
45
|
+
ignore_scope_order = app.config.active_record_update_in_bulk.ignore_scope_order
|
|
46
|
+
unless ignore_scope_order == true || ignore_scope_order == false
|
|
47
|
+
raise ArgumentError, "ignore_scope_order must be true or false"
|
|
48
|
+
end
|
|
49
|
+
ActiveRecord::UpdateInBulk::Builder.ignore_scope_order = ignore_scope_order
|
|
45
50
|
end
|
|
46
|
-
ActiveRecord::UpdateInBulk::Builder.ignore_scope_order = ignore_scope_order
|
|
47
51
|
end
|
|
48
52
|
end
|
|
49
53
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-updateinbulk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bruno Carvalho
|
|
@@ -23,20 +23,6 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '8.0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: rake
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - ">="
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0'
|
|
40
26
|
description: Introduces update_in_bulk(), a method to update many records in a table
|
|
41
27
|
with different values in a single SQL statement.
|
|
42
28
|
email:
|