LoginSignup
5

More than 5 years have passed since last update.

Rails + postgresql + Partitionに対応する設定

Last updated at Posted at 2017-06-21

前提

Rails + postgresql でPartitionを切った所ハマったので備忘を残します。
※根本的にはrailsに限った話ではないと思います

ハマリポイント

端的に↓です。
登録IDつかってゴニョゴニョしたい場合、詰みます。

model.save!
model.id # saveしてもidがnilのまま & DBにはRecordが出来ている

回避方法

databse.ymlにinsert_returning=falseの設定を入れる。

config/database.yml
development:
  adapter: postgresql
  encoding: utf8
  host: <%= ENV['HOST'] %>
  database: <%= ENV['DATABASE'] %>
  port: 5432
  reconnect: false
  username: <%= ENV['USERNAME'] %>
  password: <%= ENV['PASSWORD'] %>
  pool: 10
  insert_returning: false # ←これを入れる

何故idがnilになるのか?

insert時にRETURNING区でidを返却させているのですが、これが返って来ない為、idが設定されていません。

/lib/active_record/connection_adapters/postgresql/database_statements.rb
        def sql_for_insert(sql, pk, _id_value, _sequence_name, binds)
          unless pk
            # Extract the table from the insert sql. Yuck.
            table_ref = extract_table_ref_from_insert_sql(sql)
            pk = primary_key(table_ref) if table_ref
          end

          if pk && use_insert_returning?
            sql = "#{sql} RETURNING #{quote_column_name(pk)}"
          end

          [sql, binds]
        end

なぜRETURNINGが効かないのか?
※DBの設定でRETURNINGは有効です。

ということで、原因は以下参照
https://wiki.postgresql.org/wiki/INSERT_RETURNING_vs_Partitioning

insert_returning=falseでどうなるのか?

RETURNINGが実行されず、「last_insert_id_result(sequence_name)」が実行される。
ここで、同一トランザクションかつinsert直後に最新Sequenceを取る。
ということで、これで取れるようになります。

/lib/active_record/connection_adapters/postgresql/database_statements.rb
        def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
          val = exec_query(sql, name, binds)
          if !use_insert_returning? && pk # ←ここに入る様になる
            unless sequence_name
              table_ref = extract_table_ref_from_insert_sql(sql)
              sequence_name = default_sequence_name(table_ref, pk)
              return val unless sequence_name
            end
            last_insert_id_result(sequence_name)
          else
            val
          end
        end
/lib/active_record/connection_adapters/postgresql_adapter.rb
      def last_insert_id_result(sequence_name) #:nodoc:
        ret = exec_query("SELECT currval('#{sequence_name}')", 'SQL')
        ret
      end

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
5