-
Notifications
You must be signed in to change notification settings - Fork 21.7k
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
Add prepared statements support for Mysql2Adapter
#23461
Add prepared statements support for Mysql2Adapter
#23461
Conversation
r? @chancancode (@rails-bot has picked a reviewer for you, use r? to override) |
63b18e6
to
22858f6
Compare
@@ -126,6 +141,10 @@ def supports_datetime_with_precision? | |||
version >= '5.6.4' | |||
end | |||
|
|||
def supports_json? |
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.
This seems unrelated to prepared statement support
07e428d
to
c962972
Compare
This will need to handle nodes which we consider to be unpreparable, similar to what we do in PG and SQLite. We can probably just move that logic up to the abstract adapter now. |
Do you means #23515? |
a370e7c
to
c021804
Compare
8980117
to
d7f30c2
Compare
I've just pushed mysql2 0.4.3 with important prepared statement fixes from @kamipo! |
428f612
to
86b2576
Compare
Rebase time 😊 |
end | ||
else | ||
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result| | ||
ActiveRecord::Result.new(result.fields, result.to_a) if result |
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.
In mysql2 0.4.3, result.fields
causes segv when num of rows is zero.
This issue was fixed on master by brianmario/mysql2#741.
@sodabrew Could you release mysql2 0.4.4?
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.
Done! mysql2 0.4.4 is now on Rubygems.org.
I've just posted mysql2 0.4.4 with additional prepared statement fixes from @kamipo! |
Thanks @sodabrew !! 😍 |
86b2576
to
c2c5708
Compare
Rebased on master! |
c2c5708
to
bc7fe67
Compare
Changing to 5.0 since prepared statements are disabled by default. |
# Executes the SQL statement in the context of this connection. | ||
def execute(sql, name = nil) | ||
log(sql, name) { @connection.query(sql) } | ||
end | ||
|
||
# Mysql2Adapter doesn't have to free a result after using it, but we use this method | ||
# to write stuff in an abstract way without concerning ourselves about whether it | ||
# needs to be explicitly freed or not. | ||
def execute_and_free(sql, name = nil) # :nodoc: |
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.
We can remove this method here in a different PR. This abstract class can be removed too since we only have one concrete class.
14dd46f
to
3f6574e
Compare
|
||
gem 'mysql2', '>= 0.3.18', '< 0.5' | ||
gem 'mysql2', '~> 0.4.4' |
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.
Do we need to bump mysql2 minimum version? 0.4.x is only required if prepared_statements: true
.
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.
Unfortunately, mysql2 0.4.3 was broken without prepared statements.
I would like to skip this version.
…adapter Add prepared statements support for `Mysql2Adapter`
[ci skip]
Hurray! |
- Rename max to statement_limit - Remove magic number 1000 from everywhere - Defined StatementPool::DEFAULT_STATEMENT_LIMIT and started using it everywhere
- Rename max to statement_limit - Remove magic number 1000 from everywhere - Defined StatementPool::DEFAULT_STATEMENT_LIMIT and started using it everywhere Signed-off-by: Jeremy Daer <jeremydaer@gmail.com>
Since #23461, all adapters supports prepared statements, so that clears the prepared statements cache is no longer database specific. Actually, I struggled to identify the cause of random CI failure in #23461, that was missing `@statements.clear` in `clear_cache!`. This extracts `clear_cache!` to ensure the common concerns in the abstract adapter.
Revert "Revert "Merge pull request rails#39613 from kamipo/where_with_custom_operator"" This reverts commit da02291. ```ruby posts = Post.order(:id) posts.where("id >": 9).pluck(:id) # => [10, 11] posts.where("id >=": 9).pluck(:id) # => [9, 10, 11] posts.where("id <": 3).pluck(:id) # => [1, 2] posts.where("id <=": 3).pluck(:id) # => [1, 2, 3] ``` From type casting and table/column name resolution's point of view, `where("created_at >=": time)` is better alternative than `where("created_at >= ?", time)`. ```ruby class Post < ActiveRecord::Base attribute :created_at, :datetime, precision: 3 end time = Time.now.utc # => 2020-06-24 10:11:12.123456 UTC Post.create!(created_at: time) # => #<Post id: 1, created_at: "2020-06-24 10:11:12.123000"> # SELECT `posts`.* FROM `posts` WHERE (created_at >= '2020-06-24 10:11:12.123456') Post.where("created_at >= ?", time) # => [] # SELECT `posts`.* FROM `posts` WHERE `posts`.`created_at` >= '2020-06-24 10:11:12.123000' Post.where("created_at >=": time) # => [#<Post id: 1, created_at: "2020-06-24 10:11:12.123000">] ``` As a main contributor of the predicate builder area, I'd recommend to people use the hash syntax, the hash syntax also have other useful effects (making boundable queries, unscopeable queries, hash-like relation merging friendly, automatic other table references detection). * Making boundable queries While working on rails#23461, I realized that Active Record doesn't generate boundable queries perfectly, so I've been improving generated queries to be boundable for a long time. e.g. rails#26117 7d53993 rails#39219 Now, `where` with the hash syntax will generate boundable queries perfectly. I also want to generate boundable queries with a comparison operator in a third party gem, but currently there is no other way than calling `predicate_builder` directly. kufu/activerecord-bitemporal#62 * Unscopeable queries, Hash-like relation merging friendly Unscopeable, and Hash-like merging friendly queries are relying on where clause is an array of attr with value, and attr name is normalized as a string (i.e. using `User.arel_table[:name]` is not preferable for `unscope` and `merge`). Example: ```ruby id = User.arel_table[:id] users = User.where(id.gt(1).and(id.lteq(10))) # no-op due to `id.gt(1).and(id.lteq(10))` is not an attr with value users.unscope(:id) ``` * Automatic other table references detection It works only for the hash syntax. ee7f666
Revert "Revert "Merge pull request rails#39613 from kamipo/where_with_custom_operator"" This reverts commit da02291. ```ruby posts = Post.order(:id) posts.where("id >": 9).pluck(:id) # => [10, 11] posts.where("id >=": 9).pluck(:id) # => [9, 10, 11] posts.where("id <": 3).pluck(:id) # => [1, 2] posts.where("id <=": 3).pluck(:id) # => [1, 2, 3] ``` From type casting and table/column name resolution's point of view, `where("created_at >=": time)` is better alternative than `where("created_at >= ?", time)`. ```ruby class Post < ActiveRecord::Base attribute :created_at, :datetime, precision: 3 end time = Time.now.utc # => 2020-06-24 10:11:12.123456 UTC Post.create!(created_at: time) # => #<Post id: 1, created_at: "2020-06-24 10:11:12.123000"> # SELECT `posts`.* FROM `posts` WHERE (created_at >= '2020-06-24 10:11:12.123456') Post.where("created_at >= ?", time) # => [] # SELECT `posts`.* FROM `posts` WHERE `posts`.`created_at` >= '2020-06-24 10:11:12.123000' Post.where("created_at >=": time) # => [#<Post id: 1, created_at: "2020-06-24 10:11:12.123000">] ``` As a main contributor of the predicate builder area, I'd recommend to people use the hash syntax, the hash syntax also have other useful effects (making boundable queries, unscopeable queries, hash-like relation merging friendly, automatic other table references detection). * Making boundable queries While working on rails#23461, I realized that Active Record doesn't generate boundable queries perfectly, so I've been improving generated queries to be boundable for a long time. e.g. rails#26117 7d53993 rails#39219 Now, `where` with the hash syntax will generate boundable queries perfectly. I also want to generate boundable queries with a comparison operator in a third party gem, but currently there is no other way than calling `predicate_builder` directly. kufu/activerecord-bitemporal#62 * Unscopeable queries, Hash-like relation merging friendly Unscopeable, and Hash-like merging friendly queries are relying on where clause is an array of attr with value, and attr name is normalized as a string (i.e. using `User.arel_table[:name]` is not preferable for `unscope` and `merge`). Example: ```ruby id = User.arel_table[:id] users = User.where(id.gt(1).and(id.lteq(10))) # no-op due to `id.gt(1).and(id.lteq(10))` is not an attr with value users.unscope(:id) ``` * Automatic other table references detection It works only for the hash syntax. ee7f666
Reopen #22415.
Currently mysql2 adapter does not support prepared statements. And mysql adapter has been removed. This means that Active Record does not support prepared statements for MySQL now.
We need to support prepared statements for MySQL. But mysql2 gem still have GC issue in prepared statements. brianmario/mysql2#694
I made default to
@prepared_statements = false
inMysql2Adapter
for does not affect default behavior. I think it is still safety.