Always set id from database when creating new record

Hi guys,

this is code from ActiveRecord::Persistance:

def _create_record(attribute_names = self.attribute_names) 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

``

I’m curious about the rational behind line no. 5. Why we set self.id to value returned from db only if it was previously empty? I couldn’t find any test nor some discussion. Also the code dates back to 2011.

I work with some legacy db where primary key is string which is modified in some database trigger. Because the self.id is set before save it isn’t assigned to proper value returned from db after trigger modification.

I can’t think of any problem if we always assign id to value returned from database.

I would love to create PR if it’s ok

thanks!

Sledujte náš blog.uol.cz, facebook.com/uol.cz, gplus.to/uolcz

Wouldn’t doing so disallow you from manually setting the ID? I know it’s a weird case but we do this in our product in a table with string IDs.

I don’t think it would. You set the id in model, it’s passed to database, and then database returns it back again

Dne pátek 21. srpna 2015 17:26:49 UTC+2 masa331 napsal(a):

I don't think it would. You set the id in model, it's passed to database, and then database returns it back again

Some of the database adapters require an additional round-trip to retrieve the newly-inserted ID; this doesn't happen if an id was supplied at record creation. Others (like MySQL) will fall back on the DB's built in "last inserted ID" mechanism, which doesn't work if you are generating IDs manually (`mysql_insert_id`, for instance, only gets a value if the id column was AUTO_INCREMENT).

For more detail, keep tracing from where you quoted. `_create_record` calls `ActiveRecord::Relation#insert`, here:

which does two things:

* if the primary key is empty and the adapter wants to use prefetch, it generates an ID

* explicitly selects the supplied (or generated) primary key value and passes it as a separate argument to the `insert` method in the connection adapter.

The default implementation of the connection adapter's `insert` method:

will automatically return the value passed in as `id_value`, if present. The MySQL and Postgres versions appear to do this as well.

A workaround for your case would be database-specific; for instance, on MySQL I'd recommend that you ensure the trigger sets `LAST_INSERT_ID` and then extract the value from the DB directly (via `SELECT LAST_INSERT_ID()`) in an `after_create`. Unfortunately, the ID is the one field that the standard, DB-independent approach to dealing with trigger-supplied data (calling `reload` after saving) won't work for. :slight_smile:

--Matt Jones

Dne pátek 21. srpna 2015 17:26:49 UTC+2 masa331 napsal(a):