Class: ActiveRecord::Base
- Inherits:
-
Object
- Object
- ActiveRecord::Base
show all
- Extended by:
- ActiveRecord::Bulkoperation::BatchUpdate::InstanceMethods
- Defined in:
- lib/activerecord_bulkoperation.rb,
lib/activerecord_bulkoperation/bulkoperation.rb,
lib/activerecord_bulkoperation/group_operations.rb,
lib/activerecord_bulkoperation/group_operations_select.rb
Instance Attribute Summary collapse
Class Method Summary
collapse
-
.build_delete_by_primary_key_sql ⇒ Object
-
.build_optimistic_delete_sql ⇒ Object
-
.build_optimistic_update_sql ⇒ Object
-
.build_update_by_primary_key_sql ⇒ Object
-
.check_group(group) ⇒ Object
-
.check_sequence ⇒ Object
-
.delete_group(group, options = {}) ⇒ Object
-
.establish_connection_with_activerecord_bulkoperation(*args) ⇒ Object
(also: establish_connection)
-
.extract_options_from_args!(args) ⇒ Object
-
.find_detail_references(table_name) ⇒ Object
-
.find_foreign_detail_tables(table_name) ⇒ Object
-
.find_foreign_master_tables(table_name) ⇒ Object
-
.find_group_by(args) ⇒ Object
old style group find like PItem.find_group_by( :i_company => [45,45,45], :i_itemno => [1,2,3], :i_size => [0,0,0] ) leads to a sql statement in the form of ‘SELECT * FROM table WHERE ((i_company, i_itemno, i_size) IN ((45, 1, 0), (45, 2, 0), (45, 3, 0)))’.
-
.flush_scheduled_operations(args = {}) ⇒ Object
-
.foreign_detail_tables ⇒ Object
-
.foreign_master_tables ⇒ Object
-
.has_id_column? ⇒ Boolean
-
.insert_group(group, _options = {}) ⇒ Object
-
.insert_on_missing_group(keys, group, _options = {}) ⇒ Object
-
.merge_group(group, options = {}) ⇒ Object
will insert or update the given array.
-
.next_sequence_value ⇒ Object
-
.sequence_exists?(name) ⇒ Boolean
-
.to_type_symbol(column) ⇒ Object
-
.update_group(group, options = {}) ⇒ Object
-
.where_tuple(symbols_tuple, values_tuples) ⇒ Object
not really compatible to the rest of ActiveRecord but it works provide parameters like this: symbols_tuple = [ :col1, :col2, :col3 ] and values_tuples = [ [1, 4, 7], [2, 5, 8], [3, 6, 9]].
Instance Method Summary
collapse
enhanced_write_lobs, execute_batch_update
Instance Attribute Details
#orginal_selected_record ⇒ Object
Returns the value of attribute orginal_selected_record.
5
6
7
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 5
def orginal_selected_record
@orginal_selected_record
end
|
Class Method Details
.build_delete_by_primary_key_sql ⇒ Object
64
65
66
67
68
69
70
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 64
def build_delete_by_primary_key_sql
keys = primary_key_columns
index = 1
"DELETE FROM #{table_name} " \
'WHERE ' + "#{keys.map { |c| string = "#{c.name} = :#{index}"; index += 1; string }.join(' AND ') } "
end
|
.build_optimistic_delete_sql ⇒ Object
72
73
74
75
76
77
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 72
def build_optimistic_delete_sql
index = 1
"DELETE FROM #{table_name} " \
'WHERE ' + "#{columns.map { |c| string = build_optimistic_where_element(index, c); index += 1; index += 1 if c.null; string }.join(' AND ') } "
end
|
.build_optimistic_update_sql ⇒ Object
79
80
81
82
83
84
85
86
87
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 79
def build_optimistic_update_sql
index = 1
"UPDATE #{table_name} " \
'SET ' +
"#{columns.map { |c| string = c.name + " = :#{index}"; index += 1; string }.join(', ') } " +
'WHERE ' +
"#{columns.map { |c| string = build_optimistic_where_element(index, c); index += 1; index += 1 if c.null; string }.join(' AND ') } " end
|
.build_update_by_primary_key_sql ⇒ Object
89
90
91
92
93
94
95
96
97
98
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 89
def build_update_by_primary_key_sql
keys = primary_key_columns
index = 1
"UPDATE #{table_name} " \
'SET ' +
"#{columns.map { |c| string = c.name + " = :#{index}"; index += 1; string }.join(', ') } " +
'WHERE ' +
"#{keys.map { |c| string = "#{c.name} = :#{index}"; index += 1; string }.join(' AND ') } "
end
|
.check_group(group) ⇒ Object
71
72
73
74
75
76
77
78
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 71
def check_group(group)
unless group.is_a?(Array) || group.is_a?(Set) || group.is_a?(ActiveRecord::Relation)
raise ArgumentError, "Array expected. Got #{group.class.name}."
end
unless group.reject { |i| i.is_a? self }.empty?
raise ArgumentError, "only records of #{name} expected. Unexpected #{group.reject { |i| i.is_a? self }.map { |i| i.class.name }.uniq.join(',')} found."
end
end
|
.check_sequence ⇒ Object
29
30
31
32
33
34
35
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 29
def check_sequence
if sequence_exists?("#{table_name}_seq")
"#{table_name}_seq"
else
sequence_name
end
end
|
.delete_group(group, options = {}) ⇒ Object
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 232
def delete_group(group, options = {})
check_group(group)
optimistic = options[:optimistic]
optimistic = true if optimistic.nil?
to_delete = group.reject(&:new_record?)
if optimistic && !to_delete.reject(&:orginal_selected_record).empty?
raise NoOrginalRecordFound
end
sql = optimistic ? build_optimistic_delete_sql : build_delete_by_primary_key_sql
types = []
if optimistic
columns.each do |c|
type = to_type_symbol(c)
types << type
types << type if c.null
end
else
keys = primary_key_columns
keys.each do |c|
type = to_type_symbol(c)
types << type
end
end
values = []
if optimistic
to_delete.each do |record|
row = []
orginal = record.orginal_selected_record
columns.each do |c|
v = orginal[c.name]
row << v
row << v if c.null
end
values << row
end
else
keys = primary_key_columns
to_delete.each do |record|
row = keys.map { |c| record[c.name] }
values << row
end
end
count = execute_batch_update(sql, types, values, optimistic)
count
rescue ExternalDataChange => e
raise e
rescue Exception => e
raise StatementError, "#{sql} #{e.message}"
end
|
.establish_connection_with_activerecord_bulkoperation(*args) ⇒ Object
Also known as:
establish_connection
10
11
12
13
|
# File 'lib/activerecord_bulkoperation.rb', line 10
def establish_connection_with_activerecord_bulkoperation(*args)
establish_connection_without_activerecord_bulkoperation(*args)
ActiveSupport.run_load_hooks(:active_record_connection_established, connection_pool)
end
|
60
61
62
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 60
def (args) args.last.is_a?(Hash) ? args.pop : {}
end
|
.find_detail_references(table_name) ⇒ Object
54
55
56
57
58
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 54
def self.find_detail_references(table_name)
connection.find_detail_references_sql_array(table_name)
sql = sanitize_sql([sql, table_name.upcase])
find_by_sql(sql)
end
|
.find_foreign_detail_tables(table_name) ⇒ Object
48
49
50
51
52
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 48
def find_foreign_detail_tables(table_name)
arr = connection.find_foreign_detail_tables_sql_array(table_name)
sql = sanitize_sql(arr)
Array(find_by_sql(sql)).map { |e|e.table_name }
end
|
.find_foreign_master_tables(table_name) ⇒ Object
42
43
44
45
46
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 42
def find_foreign_master_tables(table_name)
arr = connection.find_foreign_master_tables_sql_array(table_name)
sql = sanitize_sql(arr)
Array(find_by_sql(sql)).map { |e|e.table_name }
end
|
.find_group_by(args) ⇒ Object
old style group find like PItem.find_group_by( :i_company => [45,45,45], :i_itemno => [1,2,3], :i_size => [0,0,0] ) leads to a sql statement in the form of ‘SELECT * FROM table WHERE ((i_company, i_itemno, i_size) IN ((45, 1, 0), (45, 2, 0), (45, 3, 0)))’
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
# File 'lib/activerecord_bulkoperation/group_operations_select.rb', line 9
def find_group_by(args)
fail 'no hash given, use Table.all() instead' if args.nil? || args.empty? || (not args.is_a?(Hash))
fail 'args is not a hash of arrays' if args.select { |k,v| k.is_a?(Symbol) || v.is_a?(Array) }.size != args.size
conditions = ( args.is_a?(Hash) && args[:conditions] ) || args
tuple_size = conditions.size
array_size = 0
conditions.each do |k,v|
array_size = v.size unless array_size > 0
fail 'not all arrays are of the same size' unless v.size == array_size
end
symbols = conditions.keys
values = Array.new(array_size) { Array.new(tuple_size) }
i = 0
(array_size*tuple_size).times do
values[i/tuple_size][i%tuple_size] = conditions.values[i%tuple_size][i/tuple_size]
i += 1
end
return where_tuple(symbols, values)
end
|
.flush_scheduled_operations(args = {}) ⇒ Object
.foreign_detail_tables ⇒ Object
63
64
65
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 63
def foreign_detail_tables
@foreign_detail_tables ||= find_foreign_detail_tables(table_name)
end
|
.foreign_master_tables ⇒ Object
67
68
69
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 67
def foreign_master_tables
@foreign_master_tables ||= find_foreign_master_tables(table_name)
end
|
.has_id_column? ⇒ Boolean
18
19
20
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 18
def has_id_column?
@has_id_column ||= columns_hash.key?('id')
end
|
.insert_group(group, _options = {}) ⇒ Object
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 80
def insert_group(group, _options = {})
group.reject(&:new_record?)
sql = "INSERT INTO #{table_name} " \
'( ' \
"#{columns.map(&:name).join(', ')} " \
') VALUES ( ' \
"#{(1..columns.count).map { |i| ":#{i}" }.join(', ')} " \
')'
types = []
columns.each do |c|
type = to_type_symbol(c)
types << type
end
values = []
group.each do |record|
row = []
columns.each do |c|
v = record.read_attribute(c.name)
row << v
end
values << row
end
result = execute_batch_update(sql, types, values)
group.each(&:unset_new_record)
result
end
|
.insert_on_missing_group(keys, group, _options = {}) ⇒ Object
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 192
def insert_on_missing_group(keys, group, _options = {})
keys = Array(primary_key) if keys.nil? || keys.empty?
sql = "
merge into #{table_name} target
using ( select #{columns.map { |c| ":#{columns.index(c) + 1} #{c.name}" }.join(', ')} from dual ) source
on ( #{keys.map { |c| "target.#{c} = source.#{c}" }.join(' and ')} )
when not matched then
insert ( #{columns.map(&:name).join(', ')} )
values( #{columns.map { |c| "source.#{c.name}" }.join(', ')} )
"
types = columns.map { |c| to_type_symbol(c) }
values = []
group.each do |record|
row = []
columns.each do |c|
v = record.read_attribute(c.name)
row << v
end
values << row
end
begin
result = execute_batch_update(sql, types, values, false)
rescue StandardError => e
raise ActiveRecord::StatementInvalid, "#{e.message}\n#{sql}"
end
group.each(&:unset_new_record)
result
end
|
.merge_group(group, options = {}) ⇒ Object
will insert or update the given array
group
array of activerecord objects
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 29
def merge_group(group, options = {})
check_group(group)
to_insert = group.select(&:new_record?)
to_update = group.reject(&:new_record?)
affected_rows = 0
unless to_insert.empty?
affected_rows += insert_group(to_insert, options)
end
unless to_update.empty?
affected_rows += update_group(to_update, options)
end
affected_rows
end
|
.next_sequence_value ⇒ Object
.sequence_exists?(name) ⇒ Boolean
22
23
24
25
26
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 22
def sequence_exists?(name)
arr = connection.find_by_sequence_name_sql_array(name)
sql = sanitize_sql(arr)
find_by_sql(sql).count == 1
end
|
.to_type_symbol(column) ⇒ Object
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 48
def to_type_symbol(column)
return :string if column.sql_type.index('CHAR')
if column.sql_type.index('DATE') || column.sql_type.index('TIMESTAMP')
return :date
end
if column.sql_type.index('NUMBER') && (column.sql_type.count(',') == 0)
return :integer
end
if column.sql_type.index('NUMBER') && (column.sql_type.count(',') == 1)
return :float
end
raise ArgumentError, "type #{column.sql_type} of #{column.name} is unsupported"
end
|
.update_group(group, options = {}) ⇒ Object
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 117
def update_group(group, options = {})
check_group(group)
optimistic = options[:optimistic]
optimistic = true if optimistic.nil?
if optimistic && !group.reject(&:orginal_selected_record).empty?
raise NoOrginalRecordFound, "#{name} ( #{table_name} )"
end
sql = optimistic ? build_optimistic_update_sql : build_update_by_primary_key_sql
types = []
columns.each do |c|
type = to_type_symbol(c)
types << type
end
if optimistic
columns.each do |c|
type = to_type_symbol(c)
types << type
types << type if c.null
end
else
keys = primary_key_columns
keys.each do |c|
type = to_type_symbol(c)
types << type
end
end
values = []
keys = primary_key_columns
group.each do |record|
row = []
columns.each do |c|
v = record.read_attribute(c.name)
row << v
end
if optimistic
orginal = record.orginal_selected_record
columns.each do |c|
v = orginal[c.name]
row << v
row << v if c.null
end
else
keys.each { |c| row << record[c.name] }
end
values << row
end
count = execute_batch_update(sql, types, values, optimistic)
count
end
|
.where_tuple(symbols_tuple, values_tuples) ⇒ Object
not really compatible to the rest of ActiveRecord but it works provide parameters like this: symbols_tuple = [ :col1, :col2, :col3 ] and values_tuples = [ [1, 4, 7], [2, 5, 8], [3, 6, 9]]
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
# File 'lib/activerecord_bulkoperation/group_operations_select.rb', line 37
def where_tuple(symbols_tuple, values_tuples)
if symbols_tuple.nil? || symbols_tuple.size == 0 || (not symbols_tuple.is_a?(Array)) || (not symbols_tuple.select { |s| not s.is_a?(Symbol) }.empty?)
fail 'no symbols given or not every entry is a symbol'
end
tuple_size = symbols_tuple.size
if values_tuples.nil? || values_tuples.size == 0 || (not values_tuples.is_a?(Array)) || (not values_tuples.select { |s| not s.is_a?(Array) }.empty?)
fail 'no values given or not every value is an array'
end
tuple_part = "(#{(['?']*tuple_size).join(',')})"
in_stmt = "(#{([tuple_part]*values_tuples.size).join(', ')})"
stmt = "(#{symbols_tuple.map { |sym| sym.to_s }.join(', ')}) IN #{in_stmt}"
res = where(stmt, *(values_tuples.flatten!))
return res
end
|
Instance Method Details
#callbacks_closed_scheduled_operation ⇒ Object
14
15
16
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 14
def callbacks_closed_scheduled_operation
(@callbacks_closed_scheduled_operation ||= Set.new)
end
|
#insert_on_missing(*unique_columns) ⇒ Object
167
168
169
170
171
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 167
def insert_on_missing( *unique_columns )
unique_columns = Array( self.class.primary_key ) if unique_columns.nil? || unique_columns.empty?
set_id_from_sequence
self.class.insert_on_missing_group( unique_columns, [self] ) > 0
end
|
#on_closed_scheduled_operation ⇒ Object
18
19
20
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 18
def on_closed_scheduled_operation
defined?(@callbacks_closed_scheduled_operation) && @callbacks_closed_scheduled_operation && @callbacks_closed_scheduled_operation.each(&:on_closed_scheduled_operation)
end
|
#optimistic_delete ⇒ Object
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 218
def optimistic_delete
fail NoPersistentRecord.new if @new_record
fail NoOrginalRecordFound.new unless orginal_selected_record
sql = self.class.build_optimistic_delete_sql
binds = get_optimistic_where_binds
types = []
for c in self.class.columns
type = self.class.to_type_symbol(c)
types << type
types << type if c.null
end
self.class.execute_batch_update(sql, types, [binds])
end
|
#optimistic_update ⇒ Object
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 192
def optimistic_update
fail NoPersistentRecord.new if @new_record
fail NoOrginalRecordFound.new unless orginal_selected_record
sql = self.class.build_optimistic_update_sql
types = []
for c in self.class.columns
type = self.class.to_type_symbol(c)
types << type
end
for c in self.class.columns
type = self.class.to_type_symbol(c)
types << type
types << type if c.null
end
binds = self.class.columns.map { |column| read_attribute(column.name) }
get_optimistic_where_binds.each { |v| binds << v }
self.class.execute_batch_update(sql, types, [binds])
end
|
#save_original ⇒ Object
7
8
9
10
11
12
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 7
def save_original
unless defined?(@orginal_selected_record) && @orginal_selected_record
@orginal_selected_record = @attributes.to_hash.clone
end
self
end
|
#schedule_delete ⇒ Object
#schedule_insert_on_missing(*unique_columns) ⇒ Object
#schedule_merge_on_change(args = {}) ⇒ Object
Checks if a active record object was changed and if schedule if for DB merge
args - Hash
return - self
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 136
def schedule_merge_on_change( args={} )
if orginal_selected_record.nil?
schedule_merge
return self
end
ignore_columns = args[:ignore_columns] || []
attributes.each_pair do |key, value|
next if ignore_columns.include?(key)
if value != orginal_selected_record[key]
schedule_merge
return self
end
end
self
end
|
#set_id_from_sequence ⇒ Object
if id column is nil a value will be fetched from the sequence and set to the id property
179
180
181
182
183
184
185
186
187
188
189
|
# File 'lib/activerecord_bulkoperation/bulkoperation.rb', line 179
def set_id_from_sequence
if self.class.has_id_column? && @attributes.fetch_value( 'id' ).nil?
new_id = self.class.next_sequence_value
if self.class.primary_key == 'id'
self.id = new_id
else
id_will_change! @attributes.write_from_user( 'id', new_id )
end
end
end
|
#unset_new_record ⇒ Object
22
23
24
|
# File 'lib/activerecord_bulkoperation/group_operations.rb', line 22
def unset_new_record
@new_record = false
end
|