Skip to content

mysqli->ping() throws an exception if the connection is broken #11897

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

Closed
TheNewHEROBRINEX opened this issue Aug 7, 2023 · 17 comments
Closed

Comments

@TheNewHEROBRINEX
Copy link

TheNewHEROBRINEX commented Aug 7, 2023

Description

$mysqli->ping() should return false if the connection is broken but instead it produces the following error:

Fatal error: Uncaught mysqli_sql_exception: Malformed packet in /home/luca/mysql-test.php:17  
Stack trace:  
#0 /home/luca/mysql-test.php(17): mysqli->ping()  
#1 {main}  
  thrown in /home/luca/mysql-test.php on line 17

Reproducing code: https://gist.github.com/TheNewHEROBRINEX/0a855115642216bc79203de5d6784c14
To simulate a broken connection you can restart the MySQL server.

PHP Version

PHP 8.2.8

Operating System

Ubuntu 23.04

@KapitanOczywisty
Copy link

Whatever error is thrown is controlled by $mysqli->report_mode with option MYSQLI_REPORT_STRICT, you can change that property to not throw exceptions. However, I'd strongly advice to handle exceptions instead of disabling them.

@TheNewHEROBRINEX
Copy link
Author

TheNewHEROBRINEX commented Aug 7, 2023

I don't think it makes sense for this function to throw exceptions, because it is usually used to check if a reconnection to the database is needed and it is therefore expected to fail some of the times. The fact that since PHP 8.1 it throws an exception makes all examples in the documentation non-working with the default mysqli reporting options.

@TheNewHEROBRINEX TheNewHEROBRINEX changed the title mysqli->ping() produces an error if the connection is broken mysqli->ping() throws an exception if the connection is broken Aug 7, 2023
@SakiTakamachi
Copy link
Member

@TheNewHEROBRINEX
What is your MySQL version?

@kamil-tekiela
Copy link
Member

Yeah, I guess it makes sense, but I wonder if it's even possible. I will have to look at it more closely. I am not very familiar with this method or how it should actually work.
Regardless, the examples in documentation don't look very helpful at all.

@TheNewHEROBRINEX
Copy link
Author

@TheNewHEROBRINEX What is your MySQL version?

/usr/sbin/mysqld Ver 8.0.33-0ubuntu0.23.04.4 for Linux on x86_64 ((Ubuntu))

@SakiTakamachi
Copy link
Member

Thanks for the MySQL version info.
I was able to reproduce it in my environment too.

I've got it down to the point where the exception message is from mysqlnd, not mysqli, but I don't see much difference between 8.1 and 7.4 in the code around it.

It is not yet possible to determine which ext is the cause, as it is quite possible that mysqli is not catching the exception, etc., but I will continue to investigate.

If anything turns up, I'll comment again.

@SakiTakamachi
Copy link
Member

SakiTakamachi commented Aug 8, 2023

It is a simply problem with the reporting options only.

I can fix it so that no exceptions are thrown.
However, we need to discuss whether this change is a "bug fix" or a "feature change".

@TheNewHEROBRINEX
Copy link
Author

It is a simply problem with the reporting options only.

I can fix it so that no exceptions are thrown. However, we need to discuss whether this change is a "bug fix" or a "feature change".

What do you intend to do? Make $mysqli->ping() behave like mysqli reporting is set MYSQLI_REPORT_OFF, regardless of its current value?

@SakiTakamachi
Copy link
Member

Yes, that is exactly right.

@TheNewHEROBRINEX
Copy link
Author

Yes, that is exactly right.

That would be acceptable for me but I think the best thing to do would be to exempt $mysqli->ping() from throwing exceptions only if the error is that the connection has been broken but to make it still throw exceptions for other errors.
In the end its new behaviour should become:

  • Case 1: the server is alive and responds to the ping -> return true
  • Case 2: the connection is broken -> return false
  • Case 3: other error -> throw exception

But there is a problem with this: the first time $mysqli->ping() fails, the error is erroneously reported as "Malformed packet" while it doesn't make sense since no packet has been actually received from the server since the connection is broken. If you try to ping the server from the second time on the correct error ("MySQL server has gone away") will be reported.

@TheNewHEROBRINEX
Copy link
Author

I think the problem is that mysqlnd_read_packet_header_and_body correctly sets that the server is gone

SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);

but then mysqlnd_protocol::send_command_handle_OK doesn't check for it and assumes the problem is a malformed packet.
SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");

@SakiTakamachi
Copy link
Member

I think it can be done, but if libmysqlclient has a similar structure, I think that modifying only mysqlnd will cause another problem.

We might try with libmysqlclient to see if we get the same result.

(I haven't used libmysqlclient so it may take some time to verify.)

@kamil-tekiela
Copy link
Member

Don't worry about libmysqliclient as we are no longer allowing it in mysqli. We can deviate from what that library does now.

@SakiTakamachi
Copy link
Member

SakiTakamachi commented Aug 8, 2023

@kamil-tekiela

Thanks for your useful information!

I'll try to see if I can fix this problem by modifying mysqlnd.

On a side note, this document may be out of date.

https://www.php.net/manual/en/mysqlinfo.library.choosing.php

@SakiTakamachi
Copy link
Member

SakiTakamachi commented Aug 8, 2023

I think the best thing to do would be to exempt $mysqli->ping() from throwing exceptions only if the error is that the connection has been broken but to make it still throw exceptions for other errors.

I think I got the solution.

#11912

@SakiTakamachi
Copy link
Member

Changes to mysqli::ping are no longer made.
See the pull request conversation for more details.

@TheNewHEROBRINEX
Copy link
Author

Changes to mysqli::ping are no longer made. See the pull request conversation for more details.

I have read the conversation, thank you for your time anyway. I appreciated that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants