From 149a36cc3948030020c8e25d7200002ad1411417 Mon Sep 17 00:00:00 2001 From: hzyy Date: Wed, 14 May 2025 03:44:43 +0200 Subject: [PATCH 1/3] fix(mysql): validate parameter count for prepared statements Add validation to ensure the number of provided parameters matches the expected count for MySQL prepared statements. This prevents protocol errors by returning an error if the counts do not match before sending the statement for execution. --- sqlx-mysql/src/connection/executor.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sqlx-mysql/src/connection/executor.rs b/sqlx-mysql/src/connection/executor.rs index 4f5af4bf6d..d08d78147c 100644 --- a/sqlx-mysql/src/connection/executor.rs +++ b/sqlx-mysql/src/connection/executor.rs @@ -123,6 +123,14 @@ impl MySqlConnection { .get_or_prepare_statement(sql) .await?; + if arguments.types.len() != metadata.parameters { + return Err(Error::Protocol(format!( + "Prepared statement expected {} parameters but {} parameters were provided", + metadata.parameters, + arguments.types.len() + ))); + } + // https://dev.mysql.com/doc/internals/en/com-stmt-execute.html self.inner.stream .send_packet(StatementExecute { @@ -137,6 +145,14 @@ impl MySqlConnection { .prepare_statement(sql) .await?; + if arguments.types.len() != metadata.parameters { + return Err(Error::Protocol(format!( + "Prepared statement expected {} parameters but {} parameters were provided", + metadata.parameters, + arguments.types.len() + ))); + } + // https://dev.mysql.com/doc/internals/en/com-stmt-execute.html self.inner.stream .send_packet(StatementExecute { From 754c178ed17bd963e9031e334668dec03e157bc4 Mon Sep 17 00:00:00 2001 From: Aleh Shapo Date: Wed, 25 Jun 2025 02:28:34 +0200 Subject: [PATCH 2/3] refactor(mysql): use err_protocol macro for error creation Replace direct Error::Protocol(format!()) calls with err_protocol! macro in MySQL connection executor. --- sqlx-mysql/src/connection/executor.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/sqlx-mysql/src/connection/executor.rs b/sqlx-mysql/src/connection/executor.rs index d08d78147c..8bbbb21cba 100644 --- a/sqlx-mysql/src/connection/executor.rs +++ b/sqlx-mysql/src/connection/executor.rs @@ -124,11 +124,13 @@ impl MySqlConnection { .await?; if arguments.types.len() != metadata.parameters { - return Err(Error::Protocol(format!( - "Prepared statement expected {} parameters but {} parameters were provided", - metadata.parameters, - arguments.types.len() - ))); + return Err( + err_protocol!( + "prepared statement expected {} parameters but {} parameters were provided", + metadata.parameters, + arguments.types.len() + ) + ); } // https://dev.mysql.com/doc/internals/en/com-stmt-execute.html @@ -146,11 +148,13 @@ impl MySqlConnection { .await?; if arguments.types.len() != metadata.parameters { - return Err(Error::Protocol(format!( - "Prepared statement expected {} parameters but {} parameters were provided", - metadata.parameters, - arguments.types.len() - ))); + return Err( + err_protocol!( + "prepared statement expected {} parameters but {} parameters were provided", + metadata.parameters, + arguments.types.len() + ) + ); } // https://dev.mysql.com/doc/internals/en/com-stmt-execute.html From abc4caa13fc808a44b54c52d705871629128d73a Mon Sep 17 00:00:00 2001 From: Aleh Shapo Date: Wed, 2 Jul 2025 20:58:58 +0200 Subject: [PATCH 3/3] test(mysql): add parameter count validation tests - Add test for too few parameters provided to query - Add test for too many parameters provided to query - Add test for parameters provided when none expected - All tests verify Error::Protocol is returned for mismatches Covers cases for issue #3774 parameter validation fix. --- tests/mysql/error.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/mysql/error.rs b/tests/mysql/error.rs index 3ee1024fc8..f75e9513a6 100644 --- a/tests/mysql/error.rs +++ b/tests/mysql/error.rs @@ -100,3 +100,50 @@ async fn it_fails_with_invalid_save_point_statement() -> anyhow::Result<()> { Ok(()) } + +#[sqlx_macros::test] +async fn it_fails_with_parameter_count_mismatch_too_few() -> anyhow::Result<()> { + let mut conn = new::().await?; + let res: Result<_, sqlx::Error> = + sqlx::query("SELECT * FROM tweet WHERE id = ? AND owner_id = ?") + .bind(1_i64) + .execute(&mut conn) + .await; + + let err = res.unwrap_err(); + + assert!(matches!(err, Error::Protocol(_)), "{err}"); + + Ok(()) +} + +#[sqlx_macros::test] +async fn it_fails_with_parameter_count_mismatch_too_many() -> anyhow::Result<()> { + let mut conn = new::().await?; + let res: Result<_, sqlx::Error> = sqlx::query("SELECT * FROM tweet WHERE id = ?") + .bind(1_i64) + .bind(2_i64) + .execute(&mut conn) + .await; + + let err = res.unwrap_err(); + + assert!(matches!(err, Error::Protocol(_)), "{err}"); + + Ok(()) +} + +#[sqlx_macros::test] +async fn it_fails_with_parameter_count_mismatch_zero_expected() -> anyhow::Result<()> { + let mut conn = new::().await?; + let res: Result<_, sqlx::Error> = sqlx::query("SELECT COUNT(*) FROM tweet") + .bind(1_i64) + .execute(&mut conn) + .await; + + let err = res.unwrap_err(); + + assert!(matches!(err, Error::Protocol(_)), "{err}"); + + Ok(()) +}