Skip to content
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

Save affected_rows on the wrapper when reading results #1383

Merged
merged 1 commit into from
Dec 2, 2024

Conversation

byroot
Copy link
Contributor

@byroot byroot commented Dec 2, 2024

Between fetching the result and accessing the affected_rows property, GC might have been triggered and might have freed some Mysql2::Statement objects. This calls mysql_stmt_close which resets the connection affected_rows to -1, which in turn is treated as an error when calling mysql_affected_rows.

client.query("SELECT 1")
# GC might trigger here
client.affected_rows # raises Mysql2::Error

cc @matthewd @zzak (This is what is causing the flaky test on Rails).

@tenderlove You think you could merge and release this? This bugs make prepare statements fundamentally unsafe to use....

Between fetching the result and accessing the affected_rows property, GC
might have been triggered and might have freed some Mysql2::Statement
objects. This calls mysql_stmt_close which resets the connection
affected_rows to -1, which in turn is treated as an error when calling
mysql_affected_rows.

```
client.query("SELECT 1")
client.affected_rows # raises Mysql2::Error
```
byroot added a commit to byroot/rails that referenced this pull request Dec 2, 2024
Ref: brianmario/mysql2#1383

The proper fix can only be upstream, however we can reduce the likeliness
of hitting the bug by only calling `#affected_rows` when strictly necessary,
meaning when we performed a query that didn't return a result set.
byroot added a commit to byroot/rails that referenced this pull request Dec 2, 2024
Ref: brianmario/mysql2#1383

The proper fix can only be upstream, however we can reduce the likeliness
of hitting the bug by only calling `#affected_rows` when strictly necessary,
meaning when we performed a query that didn't return a result set.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
byroot added a commit to byroot/rails that referenced this pull request Dec 2, 2024
Ref: brianmario/mysql2#1383

If we eagerly close uncached statements, we drastically reduce the
risk of one of them being GCed between `#query` and `#affected_rows`.

The bug is still there thouhg, and can happen when `#execute` is
used.
byroot added a commit to byroot/rails that referenced this pull request Dec 2, 2024
Ref: brianmario/mysql2#1383

The proper fix can only be upstream, however we can reduce the likeliness
of hitting the bug by only calling `#affected_rows` when strictly necessary,
meaning when we performed a query that didn't return a result set.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
byroot added a commit to byroot/rails that referenced this pull request Dec 2, 2024
Ref: brianmario/mysql2#1383

If we eagerly close uncached statements, we drastically reduce the
risk of one of them being GCed between `#query` and `#affected_rows`.

The bug is still there thouhg, and can happen when `#execute` is
used.
byroot added a commit to byroot/rails that referenced this pull request Dec 2, 2024
Ref: brianmario/mysql2#1383

The proper fix can only be upstream, however we can reduce the likeliness
of hitting the bug by only calling `#affected_rows` when strictly necessary,
meaning when we performed a query that didn't return a result set.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
byroot added a commit to byroot/rails that referenced this pull request Dec 2, 2024
Ref: brianmario/mysql2#1383

If we eagerly close uncached statements, we drastically reduce the
risk of one of them being GCed between `#query` and `#affected_rows`.

The bug is still there thouhg, and can happen when `#execute` is
used.
Copy link
Collaborator

@sodabrew sodabrew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great.

@@ -1155,12 +1157,12 @@ static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
* if it was an UPDATE, DELETE, or INSERT.
*/
static VALUE rb_mysql_client_affected_rows(VALUE self) {
my_ulonglong retVal;
uint64_t retVal;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see the type changed between MySQL 5.7 and 8.0:
https://dev.mysql.com/doc/c-api/8.0/en/mysql-affected-rows.html

uint64_t
mysql_affected_rows(MYSQL *mysql)

https://dev.mysql.com/doc/c-api/5.7/en/mysql-affected-rows.html

my_ulonglong
mysql_affected_rows(MYSQL *mysql)

@sodabrew sodabrew merged commit 2583661 into brianmario:master Dec 2, 2024
20 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants