Rails
MySQL
ActiveRecord

ActiveRecord & MySQL において、auto_incrementされるidの値をどのように取得しているか

More than 1 year has passed since last update.

追ってみました。答えは mysql_insert_id() を使っているです。終わり。


コードリーディング

一応コードリーディングした時のメモを残しておきます。諸事情によりRailsのバージョンは 4.1.1 です。


activerecord-4.1.1/lib/active_record/persistence.rb

def save!(*)

create_or_update || raise(RecordNotSaved)
end

def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create_record : update_record
result != false
end

def create_record(attribute_names = @attributes.keys)
attributes_values = arel_attributes_with_values_for_create(attribute_names)

new_id = self.class.unscoped.insert attributes_values
self.id ||= new_id if self.class.primary_key

@new_record = false
id
end


self.class.unscoped.insert で新しいIDが返るので実装を探す


activerecord-4.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb

def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])

sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
value = exec_insert(sql, name, binds, pk, sequence_name)
id_value || last_inserted_id(value)
end

last_inserted_idで取得している。

ここから mysql2_adapter の世界。


activerecord-4.1.1/lib/active_record/connection_adapters/mysql2_adapter.rb

def last_inserted_id(result)

@connection.last_id
end

def connect
@connection = Mysql2::Client.new(@config)
configure_connection
end


@connectionにはMysql2::Clientが入るので、ここからはmysql2の世界


mysql2-0.3.15/ext/mysql2/client.c

/* call-seq:

* client.last_id
*
* Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE
* statement.
*/

static VALUE rb_mysql_client_last_id(VALUE self) {
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
return ULL2NUM(mysql_insert_id(wrapper->client));
}

というわけで mysql_insert_id() を使っているでした。