3
0

More than 1 year has passed since last update.

Rails6, SQL Serverでinsert_allが使えない問題

Last updated at Posted at 2022-09-26

Rails6でのSQLServerにてinsert_all!は使えるがinsert_allが使えなく、調査したのでメモ。
出るエラーは以下

Model.insert_all([{name: 'test'}])
ArgumentError: ActiveRecord::ConnectionAdapters::SQLServerAdapter does not support skipping duplicates

環境

activerecord-sqlserver-adapter (6.0.0)
activerecord (~> 6.0.0)

Railsのコードを読む

結論を先に言うと、ActiveRecord#insert_allは以下でon_duplicate: :skipという主キーが重複するレコードをスキップする引数をデフォルトで渡しているが、activerecord-sqlserver-adapterではこれをサポートしていないのが原因。

def insert_all(attributes, returning: nil, unique_by: nil)
  InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
end

少し深堀りすると、ActiveRecord#insert_allinitializerの中でサポートしているアダプタをチェックしている

def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
  ## 省略

  ensure_valid_options_for_connection! # ← ここ
end

ensure_valid_options_for_connection!の中身を見てみると、一番最初に書いたエラーを吐いている部分があり、これによって使えないっぽい。

def ensure_valid_options_for_connection!
    ## 省略
    if skip_duplicates? && !connection.supports_insert_on_duplicate_skip?
        raise ArgumentError, "#{connection.class} does not support skipping duplicates"
    end
end

supports_insert_on_duplicate_skip?の中身を見てみると、サポートしているアダプタはtrueを返すようになっている。
trueを返すのはmysql, sqlite3, postgresqlだけなので、sqlserverは使えず、ここでraiseされて使えないことがわかる

insert_all!は何故使える?

insert_all!on_duplicate: :raiseになっている

def insert_all!(attributes, returning: nil, record_timestamps: nil)
  InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
end

これによって、先ほどのensure_valid_options_for_connection!の条件がfalseになり、また重複行があった場合はActiveRecord::RecordNotUniqueが発生するので問題なく使える。

Issueがあった

実はもうIssueが立っていて、上に書いたことが説明されている

修正しようとしたPRもあるが、Draftなのでまだ使えなそう。。。

v7.0.0のリリースノートを見てもon_duplicateに関することは書いていないので、まだ使えなそうです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0