-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use #exec_insert over #exec_query because there's no field selections #36
base: master
Are you sure you want to change the base?
Conversation
Hi @jamis a small bump, hopefully you will have a chance to take a look in the coming days. Have a good weekend. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @daemonsy! Did you manage to reproduce it on rails 4 and 5 as well?
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
sql, binds = sql_for_insert(sql, pk, nil, sequence_name, binds)
exec_query(sql, name, binds)
end
AFAIK return primary key is supported for postgresql adapters only : https://github.com/jamis/bulk_insert/blob/master/README.md#return-primary-keys-postgresql-postgis
Would it work for mysql2
adapter?
It seems that at least it didn't error on the I'll test it on Rails 4/5 and also Postgres and let you know what I find |
It should be fine for postgresql I guess. Returning equivalent in MySQL For a single insertion it would make sense to retrieve However for bulk insert is more complicates: https://stackoverflow.com/q/7333524/5687152
Even if we would like to implement this combination gets quite hard in cases like the one I am facing with a MySQL DB where the autoincrement offset is changing according to the table. More details on the keywords from an old (and still used a lot) MySQL version doc: https://dev.mysql.com/doc/refman/5.6/en/information-functions.html#function_last-insert-id I am not excluding that there is a smart and straightforward way to make pg logic available in this cases. (E.g. it could make sense to use this combination if you can easily retrieve the autoincrement offset and you retrieve the row diff and the last id within the same transaction as the insert) |
Sorry to be slow to chime in here. Yeah, the "return primary keys" is Postgres-only, and as @mberlanda has pointed out, it's non-trivial to implement in MySQL (or, AFAIK, other databases in general). I think the right way forward, here, is to only use (Interestingly, the default implementation of |
I have checked the current implementation of def exec_query(sql, name = "SQL", binds = [], prepare: false)
if without_prepared_statement?(binds)
execute_and_free(sql, name) do |result|
ActiveRecord::Result.new(result.fields, result.to_a) if result
end
else
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
ActiveRecord::Result.new(result.fields, result.to_a) if result
end
end
end This is preventing from raising I will suggest to create a custom error and raise when return primary_key is provided with adapters other than pg. I can do a quick pr! |
Hey @mberlanda @jamis sorry I haven't dug into the code, I see that Mauro is on top of it already. To take a step back, the issue I uncovered was that At that point, my thinking is just that Following Mauro's investigation and reading the code for the different adapters, that seems to be case. In PG's case, **NB: PG's exec_insert seem to have something that deals with the return_primary_key option. I'll take a look. ** I agree with @jamis 's comment, we should just use the two methods accordingly. I can update the PR to make sure it works with both PG + (return primary keys) and MySQL. exec_queryPGdef exec_query(sql, name = "SQL", binds = [], prepare: false)
execute_and_clear(sql, name, binds, prepare: prepare) do |result|
types = {}
fields = result.fields
fields.each_with_index do |fname, i|
ftype = result.ftype i
fmod = result.fmod i
types[fname] = get_oid_type(ftype, fmod, fname)
end
ActiveRecord::Result.new(fields, result.values, types)
end
end MySQLdef exec_query(sql, name = "SQL", binds = [], prepare: false)
if without_prepared_statement?(binds)
execute_and_free(sql, name) do |result|
ActiveRecord::Result.new(result.fields, result.to_a) if result
end
else
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
ActiveRecord::Result.new(result.fields, result.to_a) if result
end
end
end |
Sorry I haven't acted on this. Will try to rally sometime to do it within this week. |
ok! Don't worry! I hope I will have the time to finish soon #37 to allow testing against the real database without mocking the adapter. In this case we should be able to validate that this has the expected behaviour for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hi @daemonsy I updated the test matrix to validate any future change against database instances. Could you please validate if this change is still needed in v1.9.0?
In case, you may rebase your change and it should be safe after CI passes. Thanks!
What's up?
Hi there,
Thanks for the gem! It's awesome. While I was trying it, I got an error on calling
worker.save!
.Investigation
It seems that the basic skeleton code of
Connection#exec_query
expects a result set that has field selections. However, since we're doing a bulk insert, it doesn't seem like this will work.However, there is an
Connection#exec_insert
method that does not expect return results.I suspect
exec_query
is used because ofreturn_primary_keys
where we call aSELECT
after the insert.What this does
exec_query
when there primary keys are requiredexec_insert
otherwise