前提
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