Skip to content

Commit 593364f

Browse files
authored
fix(mysql): handle multiple waiting results correctly (#1439)
* test(mysql): add test case for pending rows and dropped transaction * fix(mysql): handle multiple waiting results correctly
1 parent 24c0d52 commit 593364f

File tree

5 files changed

+81
-23
lines changed

5 files changed

+81
-23
lines changed

sqlx-core/src/mysql/connection/executor.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::error::Error;
44
use crate::executor::{Execute, Executor};
55
use crate::ext::ustr::UStr;
66
use crate::logger::QueryLogger;
7-
use crate::mysql::connection::stream::Busy;
7+
use crate::mysql::connection::stream::Waiting;
88
use crate::mysql::io::MySqlBufExt;
99
use crate::mysql::protocol::response::Status;
1010
use crate::mysql::protocol::statement::{
@@ -93,7 +93,7 @@ impl MySqlConnection {
9393
let mut logger = QueryLogger::new(sql, self.log_settings.clone());
9494

9595
self.stream.wait_until_ready().await?;
96-
self.stream.busy = Busy::Result;
96+
self.stream.waiting.push_back(Waiting::Result);
9797

9898
Ok(Box::pin(try_stream! {
9999
// make a slot for the shared column data
@@ -146,12 +146,12 @@ impl MySqlConnection {
146146
continue;
147147
}
148148

149-
self.stream.busy = Busy::NotBusy;
149+
self.stream.waiting.pop_front();
150150
return Ok(());
151151
}
152152

153153
// otherwise, this first packet is the start of the result-set metadata,
154-
self.stream.busy = Busy::Row;
154+
*self.stream.waiting.front_mut().unwrap() = Waiting::Row;
155155

156156
let num_columns = packet.get_uint_lenenc() as usize; // column count
157157

@@ -179,11 +179,11 @@ impl MySqlConnection {
179179

180180
if eof.status.contains(Status::SERVER_MORE_RESULTS_EXISTS) {
181181
// more result sets exist, continue to the next one
182-
self.stream.busy = Busy::Result;
182+
*self.stream.waiting.front_mut().unwrap() = Waiting::Result;
183183
break;
184184
}
185185

186-
self.stream.busy = Busy::NotBusy;
186+
self.stream.waiting.pop_front();
187187
return Ok(());
188188
}
189189

sqlx-core/src/mysql/connection/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ mod executor;
1616
mod stream;
1717
mod tls;
1818

19-
pub(crate) use stream::{Busy, MySqlStream};
19+
pub(crate) use stream::{MySqlStream, Waiting};
2020

2121
const MAX_PACKET_SIZE: u32 = 1024;
2222

sqlx-core/src/mysql/connection/stream.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::VecDeque;
12
use std::ops::{Deref, DerefMut};
23

34
use bytes::{Buf, Bytes};
@@ -16,15 +17,13 @@ pub struct MySqlStream {
1617
pub(crate) server_version: (u16, u16, u16),
1718
pub(super) capabilities: Capabilities,
1819
pub(crate) sequence_id: u8,
19-
pub(crate) busy: Busy,
20+
pub(crate) waiting: VecDeque<Waiting>,
2021
pub(crate) charset: CharSet,
2122
pub(crate) collation: Collation,
2223
}
2324

2425
#[derive(Debug, PartialEq, Eq)]
25-
pub(crate) enum Busy {
26-
NotBusy,
27-
26+
pub(crate) enum Waiting {
2827
// waiting for a result set
2928
Result,
3029

@@ -65,7 +64,7 @@ impl MySqlStream {
6564
}
6665

6766
Ok(Self {
68-
busy: Busy::NotBusy,
67+
waiting: VecDeque::new(),
6968
capabilities,
7069
server_version: (0, 0, 0),
7170
sequence_id: 0,
@@ -80,32 +79,32 @@ impl MySqlStream {
8079
self.stream.flush().await?;
8180
}
8281

83-
while self.busy != Busy::NotBusy {
84-
while self.busy == Busy::Row {
82+
while !self.waiting.is_empty() {
83+
while self.waiting.front() == Some(&Waiting::Row) {
8584
let packet = self.recv_packet().await?;
8685

8786
if packet[0] == 0xfe && packet.len() < 9 {
8887
let eof = packet.eof(self.capabilities)?;
8988

90-
self.busy = if eof.status.contains(Status::SERVER_MORE_RESULTS_EXISTS) {
91-
Busy::Result
89+
if eof.status.contains(Status::SERVER_MORE_RESULTS_EXISTS) {
90+
*self.waiting.front_mut().unwrap() = Waiting::Result;
9291
} else {
93-
Busy::NotBusy
92+
self.waiting.pop_front();
9493
};
9594
}
9695
}
9796

98-
while self.busy == Busy::Result {
97+
while self.waiting.front() == Some(&Waiting::Result) {
9998
let packet = self.recv_packet().await?;
10099

101100
if packet[0] == 0x00 || packet[0] == 0xff {
102101
let ok = packet.ok()?;
103102

104103
if !ok.status.contains(Status::SERVER_MORE_RESULTS_EXISTS) {
105-
self.busy = Busy::NotBusy;
104+
self.waiting.pop_front();
106105
}
107106
} else {
108-
self.busy = Busy::Row;
107+
*self.waiting.front_mut().unwrap() = Waiting::Row;
109108
self.skip_result_metadata(packet).await?;
110109
}
111110
}
@@ -150,7 +149,7 @@ impl MySqlStream {
150149
// TODO: packet joining
151150

152151
if payload[0] == 0xff {
153-
self.busy = Busy::NotBusy;
152+
self.waiting.pop_front();
154153

155154
// instead of letting this packet be looked at everywhere, we check here
156155
// and emit a proper Error

sqlx-core/src/mysql/transaction.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use futures_core::future::BoxFuture;
22

33
use crate::error::Error;
44
use crate::executor::Executor;
5-
use crate::mysql::connection::Busy;
5+
use crate::mysql::connection::Waiting;
66
use crate::mysql::protocol::text::Query;
77
use crate::mysql::{MySql, MySqlConnection};
88
use crate::transaction::{
@@ -57,7 +57,7 @@ impl TransactionManager for MySqlTransactionManager {
5757
let depth = conn.transaction_depth;
5858

5959
if depth > 0 {
60-
conn.stream.busy = Busy::Result;
60+
conn.stream.waiting.push_back(Waiting::Result);
6161
conn.stream.sequence_id = 0;
6262
conn.stream
6363
.write_packet(Query(&*rollback_ansi_transaction_sql(depth)));

tests/mysql/mysql.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,62 @@ async fn test_issue_622() -> anyhow::Result<()> {
387387

388388
Ok(())
389389
}
390+
391+
#[sqlx_macros::test]
392+
async fn it_can_work_with_transactions() -> anyhow::Result<()> {
393+
let mut conn = new::<MySql>().await?;
394+
conn.execute("CREATE TEMPORARY TABLE users (id INTEGER PRIMARY KEY);")
395+
.await?;
396+
397+
// begin .. rollback
398+
399+
let mut tx = conn.begin().await?;
400+
sqlx::query("INSERT INTO users (id) VALUES (?)")
401+
.bind(1_i32)
402+
.execute(&mut tx)
403+
.await?;
404+
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users")
405+
.fetch_one(&mut tx)
406+
.await?;
407+
assert_eq!(count, 1);
408+
tx.rollback().await?;
409+
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users")
410+
.fetch_one(&mut conn)
411+
.await?;
412+
assert_eq!(count, 0);
413+
414+
// begin .. commit
415+
416+
let mut tx = conn.begin().await?;
417+
sqlx::query("INSERT INTO users (id) VALUES (?)")
418+
.bind(1_i32)
419+
.execute(&mut tx)
420+
.await?;
421+
tx.commit().await?;
422+
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users")
423+
.fetch_one(&mut conn)
424+
.await?;
425+
assert_eq!(count, 1);
426+
427+
// begin .. (drop)
428+
429+
{
430+
let mut tx = conn.begin().await?;
431+
432+
sqlx::query("INSERT INTO users (id) VALUES (?)")
433+
.bind(2)
434+
.execute(&mut tx)
435+
.await?;
436+
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users")
437+
.fetch_one(&mut tx)
438+
.await?;
439+
assert_eq!(count, 2);
440+
// tx is dropped
441+
}
442+
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users")
443+
.fetch_one(&mut conn)
444+
.await?;
445+
assert_eq!(count, 1);
446+
447+
Ok(())
448+
}

0 commit comments

Comments
 (0)