()
| 177 | |
| 178 | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| 179 | async fn raw_wire_protocol_bind_errors_are_synchronized() -> Result<()> { |
| 180 | let server = PgliteServer::temporary_tcp()?; |
| 181 | let addr = server.tcp_addr().context("server should use TCP")?; |
| 182 | |
| 183 | tokio::task::spawn_blocking(move || -> Result<()> { |
| 184 | let mut stream = TcpStream::connect(addr).context("connect raw protocol socket")?; |
| 185 | stream.set_read_timeout(Some(Duration::from_secs(120)))?; |
| 186 | stream.set_write_timeout(Some(Duration::from_secs(10)))?; |
| 187 | |
| 188 | stream |
| 189 | .write_all(&startup_message()) |
| 190 | .context("write startup message")?; |
| 191 | let startup = read_until_ready(&mut stream).context("read startup response")?; |
| 192 | assert!( |
| 193 | startup.iter().any(|msg| msg.tag == b'R'), |
| 194 | "startup should include AuthenticationOk" |
| 195 | ); |
| 196 | assert_eq!(startup.last().map(|msg| msg.tag), Some(b'Z')); |
| 197 | |
| 198 | stream |
| 199 | .write_all( |
| 200 | &[ |
| 201 | parse_statement("typed_int", "SELECT $1::int4 AS value"), |
| 202 | sync(), |
| 203 | ] |
| 204 | .concat(), |
| 205 | ) |
| 206 | .context("write Parse + Sync")?; |
| 207 | let parsed = read_until_ready(&mut stream).context("read Parse response")?; |
| 208 | assert_message_tags_ignoring_parameter_status(&parsed, b"1Z"); |
| 209 | |
| 210 | stream |
| 211 | .write_all( |
| 212 | &[ |
| 213 | bind_statement("", "typed_int", &["not_an_int"]), |
| 214 | describe_portal(""), |
| 215 | execute_portal(""), |
| 216 | sync(), |
| 217 | ] |
| 218 | .concat(), |
| 219 | ) |
| 220 | .context("write invalid Bind batch")?; |
| 221 | let invalid_bind = read_until_ready(&mut stream).context("read invalid Bind response")?; |
| 222 | assert_eq!(invalid_bind.last().map(|msg| msg.tag), Some(b'Z')); |
| 223 | assert!( |
| 224 | invalid_bind.iter().all(|msg| msg.tag != b'2'), |
| 225 | "invalid Bind must not emit BindComplete" |
| 226 | ); |
| 227 | assert_eq!(first_error_code(&invalid_bind).as_deref(), Some("22P02")); |
| 228 | |
| 229 | stream |
| 230 | .write_all(&[bind_statement("", "typed_int", &[]), sync()].concat()) |
| 231 | .context("write wrong parameter count Bind batch")?; |
| 232 | let wrong_count = read_until_ready(&mut stream).context("read wrong Bind response")?; |
| 233 | assert_eq!(wrong_count.last().map(|msg| msg.tag), Some(b'Z')); |
| 234 | assert!( |
| 235 | wrong_count.iter().all(|msg| msg.tag != b'2'), |
| 236 | "wrong parameter count must not emit BindComplete" |
nothing calls this directly
no test coverage detected