Class: ActiveRecord::Base
- Inherits:
-
Object
- Object
- ActiveRecord::Base
- Defined in:
- lib/activerecord/bulkwrite.rb
Class Method Summary collapse
-
.bulk_write(fields, rows, upsert = nil) ⇒ Object
Insert or update a batch of rows.
Class Method Details
.bulk_write(fields, rows, upsert = nil) ⇒ Object
Insert or update a batch of rows. When upsert is nil, insert only. Otherwise insert or update on conflict.
Parameters: fields: an array of field names to insert value rows: an array of array of values to insert. The element, values array,
must correspond with the fields parameter.
upsert:
A hash with the following keys::
conflict: an array of field names on which a unique constrain may fail the
insert
update: an array of field names to update when conflict happens. If
omitted, it will be "fields - upsert[:conflict]".
where: where clause to determine if a row will be updated when
conflict happens. "EXCLUDED" is used for referencing the row
that failed in insert. If omitted, update all rows that have
conflict.
Return: The number of affected rows
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/activerecord/bulkwrite.rb', line 25 def self.bulk_write(fields, rows, upsert = nil) return 0 if rows.empty? columns = fields.map {|name| column_for_attribute(name) } rows = rows.map do |row| values = row.map.with_index do |val, i| # NOTE: Here quote method treats val as a Ruby value from # Value#type_cast_from_user, and thus won't convert a String time to # Time object before passing the string time to database. # That's OK for a time string in UTC, but NOT FOR LOCAL TIME, since # ActiveRecord saves time as type "DATETIME WITHOUT TIMEZONE" in database! connection.quote(val, columns[i]) end.join ', ' "(#{ values })" end field_list = fields.map{|e| %Q("#{e}") }.join ', ' sql = "INSERT INTO #{ table_name } (#{ field_list }) VALUES #{ rows.join ', ' }" if upsert if !upsert[:update] update = fields.map(&:to_s) - upsert[:conflict].map(&:to_s) else update = upsert[:update] end update = update.map {|field| "#{ field } = EXCLUDED.#{ field }" } sql += " ON CONFLICT (#{ upsert[:conflict].join ', ' }) DO UPDATE SET #{ update.join ', ' }" if upsert[:where] sql += " WHERE #{ upsert[:where] }" end end # res is a PG::Result object. See # https://deveiate.org/code/pg/PG/Result.html # for its details. res = connection.execute(sql) res.cmd_tuples end |