module ActiveRecord module Bulkoperation module BatchUpdate module InstanceMethods def execute_batch_update(sql, types, values, optimistic = true) fail ArgumentError.new('String expected') unless sql.is_a? String types = [types] unless types.is_a? Array fail ArgumentError.new('Array of Symbol expected') unless types.select { |i| not i.is_a? Symbol }.empty? fail ArgumentError.new('Array expected') unless values.is_a? Array values = [values] unless values.select { |i| not i.is_a? Array }.empty? unless values.select { |i| not i.count == types.count }.empty? fail ArgumentError.new('types.count must be equal to arr.count for every arr in values') end unless connection.adapter_name == 'Oracle' || connection.adapter_name == 'OracleEnhanced' fail "Operation is provided only on Oracle connections. (adapter_name is #{connection.adapter_name})" end oci_conn = connection.raw_connection if connected? fail 'Unable to access the raw OCI connection.' unless oci_conn cursor = oci_conn.parse(sql) fail "Unable to obtain cursor for this statement:\n#{sql}." unless cursor affected_rows = 0 begin values.each do |row| (0..row.count - 1).each do |i| bind(cursor, i + 1, types[i], row[i]) end result = cursor.exec #result = 1 if result != 1 and optimistic msg = sql i = 1 row.each do |v| val = get_value(v) msg = msg.gsub(":#{i},", connection.quote(val) + ',') msg = msg.gsub(":#{i} ", connection.quote(val) + ' ') i += 1 end i -= 1 msg = msg.gsub(":#{i}", connection.quote(get_value(row.last))) fail ExternalDataChange.new("The record you want to update was updated by another process.\n#{msg}") end affected_rows += result connection.send(:log, sql, 'Update') {} end ensure cursor.close end affected_rows end private def get_value(value) if(value.respond_to? :value) value.value else value end end def bind(cursor, index, type, input_value) value = get_value(input_value) if type == :string cursor.bind_param(":#{index}", value, String) elsif type == :integer cursor.bind_param(":#{index}", value, Fixnum) elsif type == :float cursor.bind_param(":#{index}", value, Float) elsif type == :date if value.nil? cursor.bind_param(":#{index}", nil, Date) else if ( value.kind_of?(DateTime) or value.kind_of?(Time)) and value.hour == 0 and value.min == 0 and value.sec == 0 cursor.bind_param(":#{index}", value, Date) else cursor.bind_param(":#{index}", value, DateTime) end end else fail ArgumentError.new("unsupported type #{type}") end end end end end end