Skip to content

Instantly share code, notes, and snippets.

@palkan
Last active January 28, 2020 17:39
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save palkan/9f72eada444aca2457213924fbf043ae to your computer and use it in GitHub Desktop.
Save palkan/9f72eada444aca2457213924fbf043ae to your computer and use it in GitHub Desktop.
Active Record batch_insert
# Add batch insert functionality to the model.
#
# Example:
#
# class User < ActiveRecord::Base
# include PgBatchInsert
# end
#
# User.pg_batch_insert(
# %w[name email],
# [
# ["Jack", "jack@rails.dev"],
# ["Alice", "alice@rails.dev"]
# ]
# )
module PgBatchInsert
extend ActiveSupport::Concern
module ClassMethods
def pg_batch_insert(attributes, data, on_conflict: nil, returning: nil)
raise ArgumentError, "Values must not be empty" if data.empty?
columns = columns_for_attributes(attributes)
fields_str = quote_column_names(columns)
values_str = quote_many_records(columns, data)
sql = <<~SQL
INSERT INTO #{quoted_table_name}
(#{fields_str})
VALUES #{values_str}
SQL
unless on_conflict.nil?
on_conflict = "DO NOTHING" if on_conflict == :skip
sql += " ON CONFLICT #{on_conflict}"
end
unless returning.nil?
sql += " RETURNING #{returning}"
end
connection.execute(sql)
end
def columns_for_attributes(attributes)
attributes.map do |attribute|
column_for_attribute(attribute)
end
end
def quote_column_names(columns)
columns.map do |column|
connection.quote_column_name(column.name)
end.join(",")
end
def quote_record(columns, record_values)
values_str = record_values.each_with_index.map do |value, i|
type = connection.lookup_cast_type_from_column(columns[i])
connection.quote(type.serialize(value))
end.join(",")
"(#{values_str})"
end
def quote_many_records(columns, data)
data.map { |values| quote_record(columns, values) }.join(",")
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment