Suppose there's a table
create table users (
id integer primary key,
name text,
hireable bool,
created_at date_time
)
Then you can do insert in batch:
fields = %w(id name hireable created_at)
rows = [
[1, "Bob's", true, now - 10],
[2, nil, "false", (now - 1).utc.iso8601],
]
result = User.bulk_write(fields, rows)
The result is the effected(inserted) rows.
It's just a normal batch insert and won't do upsert and thus doesn't have special requirement for PostgreSQL at all!
The method assumes rows of values are already in proper Ruby types and convert them to strings/types suitable for database. You need pay attention here: if your input for time is a string, it must be a UTC time, not a local time. See comment above for "connection.quote" for why.
When you do upsert, you pass the 3rd parameter upsert
:
upsert = { :conflict => [:id], :where => "users.id > 2 AND EXCLUDED.name IS NOT NULL" }
result = User.bulk_write(fields, rows, :upsert => upsert)
The only required field of upsert is conflict. See comment in code for the parameter's meanings. If you know PostgreSQL 9.5's upsert syntax, you understand it even more. Here it is: https://www.postgresql.org/docs/9.5/static/sql-insert.html#SQL-ON-CONFLICT
It's tested against ActiveRecord 4.2, but should work on 4.x as well as 5. You should test it on other versions before using it. The above test file is in minitest. Most of the test code is obvious except the init
method, which is just for init test database from a spec. Its implementation is omitted but easy to do with AcitveRecord::Base#create
. You could leverage the file for testing.
Moreover, PostgreSQL 9.5 and later is required for upsert, since it depends on PostgreSQL's native support for upsert. But if you insert only, then PostgreSQL is not a requirement and MySQL also works.