1use std::collections::{BTreeMap, BTreeSet};
99use std::fmt;
100use std::io::{ErrorKind, Read, Write};
101use std::net::TcpStream;
102use std::time::{Duration, Instant};
103
104use anyhow::{anyhow, bail};
105use bytes::{BufMut, BytesMut};
106use mz_ore::collections::CollectionExt;
107use postgres_protocol::IsNull;
108use postgres_protocol::message::backend::Message;
109use postgres_protocol::message::frontend;
110use regex::Regex;
111use serde::{Deserialize, Serialize};
112use tokio_postgres::fallible_iterator::FallibleIterator;
113
114struct PgConn {
115 stream: TcpStream,
116 recv_buf: BytesMut,
117 send_buf: BytesMut,
118 timeout: Duration,
119 verbose: bool,
120}
121
122impl PgConn {
123 fn new<'a>(
124 addr: &str,
125 user: &'a str,
126 timeout: Duration,
127 verbose: bool,
128 mut options: Vec<(&'a str, &'a str)>,
129 ) -> anyhow::Result<Self> {
130 let mut conn = Self {
131 stream: TcpStream::connect(addr)?,
132 recv_buf: BytesMut::new(),
133 send_buf: BytesMut::new(),
134 timeout,
135 verbose,
136 };
137
138 conn.stream.set_read_timeout(Some(timeout))?;
139 options.insert(0, ("user", user));
140 options.insert(0, ("welcome_message", "off"));
141 conn.send(|buf| frontend::startup_message(options, buf).unwrap())?;
142 match conn.recv()?.1 {
143 Message::AuthenticationOk => {}
144 _ => bail!("expected AuthenticationOk"),
145 };
146 conn.until(
147 vec!["ReadyForQuery"],
148 vec!['C', 'S', 'M'],
149 BTreeSet::new(),
150 &BTreeSet::new(),
151 )?;
152 Ok(conn)
153 }
154
155 fn send<F: FnOnce(&mut BytesMut)>(&mut self, f: F) -> anyhow::Result<()> {
156 self.send_buf.clear();
157 f(&mut self.send_buf);
158 self.stream.write_all(&self.send_buf)?;
159 Ok(())
160 }
161 fn until(
162 &mut self,
163 until: Vec<&str>,
164 err_field_typs: Vec<char>,
165 ignore: BTreeSet<String>,
166 parameter_status: &BTreeSet<String>,
167 ) -> anyhow::Result<Vec<String>> {
168 let mut msgs = Vec::with_capacity(until.len());
169 for expect in until {
170 loop {
171 let (ch, msg) = match self.recv() {
172 Ok((ch, msg)) => (ch, msg),
173 Err(err) => bail!("{}: waiting for {}, saw {:#?}", err, expect, msgs),
174 };
175 let (typ, args) = match msg {
176 Message::ReadyForQuery(body) => (
177 "ReadyForQuery",
178 serde_json::to_string(&ReadyForQuery {
179 status: char::from(body.status()).to_string(),
180 })?,
181 ),
182 Message::RowDescription(body) => (
183 "RowDescription",
184 serde_json::to_string(&RowDescription {
185 fields: body
186 .fields()
187 .map(|f| {
188 Ok(Field {
189 name: f.name().to_string(),
190 })
191 })
192 .collect()
193 .unwrap(),
194 })?,
195 ),
196 Message::DataRow(body) => {
197 let buf = body.buffer();
198 (
199 "DataRow",
200 serde_json::to_string(&DataRow {
201 fields: body
202 .ranges()
203 .map(|range| {
204 match range {
205 Some(range) => {
206 Ok(String::from_utf8(
208 buf[range.start..range.end].to_vec(),
209 )
210 .unwrap_or_else(|_| {
211 format!(
212 "{:?}",
213 buf[range.start..range.end].to_vec()
214 )
215 }))
216 }
217 None => Ok("NULL".into()),
218 }
219 })
220 .collect()
221 .unwrap(),
222 })?,
223 )
224 }
225 Message::CommandComplete(body) => (
226 "CommandComplete",
227 serde_json::to_string(&CommandComplete {
228 tag: body.tag().unwrap().to_string(),
229 })?,
230 ),
231 Message::ParseComplete => ("ParseComplete", "".to_string()),
232 Message::BindComplete => ("BindComplete", "".to_string()),
233 Message::PortalSuspended => ("PortalSuspended", "".to_string()),
234 Message::ErrorResponse(body) => (
235 "ErrorResponse",
236 serde_json::to_string(&ErrorResponse {
237 fields: body
238 .fields()
239 .filter_map(|f| {
240 let typ = char::from(f.type_());
241 if err_field_typs.contains(&typ) {
242 Ok(Some(ErrorField {
243 typ,
244 value: String::from_utf8_lossy(f.value_bytes())
245 .into_owned(),
246 }))
247 } else {
248 Ok(None)
249 }
250 })
251 .collect()
252 .unwrap(),
253 })?,
254 ),
255 Message::NoticeResponse(body) => (
256 "NoticeResponse",
257 serde_json::to_string(&ErrorResponse {
258 fields: body
259 .fields()
260 .filter_map(|f| {
261 let typ = char::from(f.type_());
262 if err_field_typs.contains(&typ) {
263 Ok(Some(ErrorField {
264 typ,
265 value: String::from_utf8_lossy(f.value_bytes())
266 .into_owned(),
267 }))
268 } else {
269 Ok(None)
270 }
271 })
272 .collect()
273 .unwrap(),
274 })?,
275 ),
276 Message::CopyOutResponse(body) => (
277 "CopyOut",
278 serde_json::to_string(&CopyOut {
279 format: format_name(body.format()),
280 column_formats: body
281 .column_formats()
282 .map(|format| Ok(format_name(format)))
283 .collect()
284 .unwrap(),
285 })?,
286 ),
287 Message::CopyInResponse(body) => (
288 "CopyIn",
289 serde_json::to_string(&CopyOut {
290 format: format_name(body.format()),
291 column_formats: body
292 .column_formats()
293 .map(|format| Ok(format_name(format)))
294 .collect()
295 .unwrap(),
296 })?,
297 ),
298 Message::CopyData(body) => (
299 "CopyData",
300 serde_json::to_string(
301 &std::str::from_utf8(body.data())
302 .map(|s| s.to_string())
303 .unwrap_or_else(|_| format!("{:?}", body.data())),
304 )?,
305 ),
306 Message::CopyDone => ("CopyDone", "".to_string()),
307 Message::ParameterDescription(body) => (
308 "ParameterDescription",
309 serde_json::to_string(&ParameterDescription {
310 parameters: body.parameters().collect().unwrap(),
311 })?,
312 ),
313 Message::ParameterStatus(body) => {
314 let name = body.name()?;
315 if !parameter_status.contains(name) {
316 continue;
317 }
318 (
319 "ParameterStatus",
320 serde_json::to_string(&ParameterStatus {
321 name: name.to_string(),
322 value: body.value()?.to_string(),
323 })?,
324 )
325 }
326 Message::NoData => ("NoData", "".to_string()),
327 Message::EmptyQueryResponse => ("EmptyQueryResponse", "".to_string()),
328 _ => ("UNKNOWN", format!("'{}'", ch)),
329 };
330 if self.verbose {
331 println!("RECV {}: {:?}", ch, typ);
332 }
333 if ignore.contains(typ) {
334 continue;
335 }
336 let mut s = typ.to_string();
337 if !args.is_empty() {
338 s.push(' ');
339 s.push_str(&args);
340 }
341 msgs.push(s);
342 if expect == typ {
343 break;
344 }
345 }
346 }
347 Ok(msgs)
348 }
349 pub fn recv(&mut self) -> anyhow::Result<(char, Message)> {
353 let mut buf = [0; 1024];
354 let until = Instant::now();
355 loop {
356 if until.elapsed() > self.timeout {
357 bail!("timeout after {:?} waiting for new message", self.timeout);
358 }
359 let mut ch: char = '0';
360 if self.recv_buf.len() > 0 {
361 ch = char::from(self.recv_buf[0]);
362 }
363 if let Some(msg) = Message::parse(&mut self.recv_buf)? {
364 return Ok((ch, msg));
365 };
366 let sz = match self.stream.read(&mut buf) {
368 Ok(n) => n,
369 Err(e) if e.kind() == ErrorKind::Interrupted => continue,
372 Err(e) => return Err(anyhow!(e)),
373 };
374 self.recv_buf.extend_from_slice(&buf[..sz]);
375 }
376 }
377}
378
379const DEFAULT_CONN: &str = "";
380
381pub struct PgTest {
382 addr: String,
383 user: String,
384 timeout: Duration,
385 conns: BTreeMap<String, PgConn>,
386 verbose: bool,
387}
388
389impl PgTest {
390 pub fn new(addr: String, user: String, timeout: Duration) -> anyhow::Result<Self> {
391 let verbose = std::env::var_os("PGTEST_VERBOSE").is_some();
392 let conn = PgConn::new(&addr, &user, timeout.clone(), verbose, vec![])?;
393 let mut conns = BTreeMap::new();
394 conns.insert(DEFAULT_CONN.to_string(), conn);
395
396 Ok(PgTest {
397 addr,
398 user,
399 timeout,
400 conns,
401 verbose,
402 })
403 }
404
405 fn get_conn(
408 &mut self,
409 name: Option<String>,
410 options: Vec<(&str, &str)>,
411 ) -> anyhow::Result<&mut PgConn> {
412 let name = name.unwrap_or_else(|| DEFAULT_CONN.to_string());
413 if !self.conns.contains_key(&name) {
414 let conn = PgConn::new(
415 &self.addr,
416 &self.user,
417 self.timeout.clone(),
418 self.verbose,
419 options,
420 )?;
421 self.conns.insert(name.clone(), conn);
422 }
423 Ok(self.conns.get_mut(&name).expect("must exist"))
424 }
425
426 pub fn send<F: Fn(&mut BytesMut)>(
427 &mut self,
428 conn: Option<String>,
429 options: Vec<(&str, &str)>,
430 f: F,
431 ) -> anyhow::Result<()> {
432 let conn = self.get_conn(conn, options)?;
433 conn.send(f)
434 }
435
436 pub fn until(
437 &mut self,
438 conn: Option<String>,
439 options: Vec<(&str, &str)>,
440 until: Vec<&str>,
441 err_field_typs: Vec<char>,
442 ignore: BTreeSet<String>,
443 parameter_status: &BTreeSet<String>,
444 ) -> anyhow::Result<Vec<String>> {
445 let conn = self.get_conn(conn, options)?;
446 conn.until(until, err_field_typs, ignore, parameter_status)
447 }
448}
449
450#[derive(Serialize)]
453pub struct ReadyForQuery {
454 pub status: String,
455}
456
457#[derive(Serialize)]
458pub struct RowDescription {
459 pub fields: Vec<Field>,
460}
461
462#[derive(Serialize)]
463pub struct Field {
464 pub name: String,
465}
466
467#[derive(Serialize)]
468pub struct DataRow {
469 pub fields: Vec<String>,
470}
471
472#[derive(Serialize)]
473pub struct CopyOut {
474 pub format: String,
475 pub column_formats: Vec<String>,
476}
477
478#[derive(Serialize)]
479pub struct ParameterDescription {
480 parameters: Vec<u32>,
481}
482
483#[derive(Serialize)]
484pub struct CommandComplete {
485 pub tag: String,
486}
487
488#[derive(Serialize)]
489pub struct ParameterStatus {
490 pub name: String,
491 pub value: String,
492}
493
494#[derive(Serialize)]
495pub struct ErrorResponse {
496 pub fields: Vec<ErrorField>,
497}
498
499#[derive(Serialize)]
500pub struct ErrorField {
501 pub typ: char,
502 pub value: String,
503}
504
505impl Drop for PgTest {
506 fn drop(&mut self) {
507 for conn in self.conns.values_mut() {
508 let _ = conn.send(frontend::terminate);
509 }
510 }
511}
512
513fn format_name<T>(format: T) -> String
514where
515 T: Copy + TryInto<u16> + fmt::Display,
516{
517 match format.try_into() {
518 Ok(0) => "text".to_string(),
519 Ok(1) => "binary".to_string(),
520 _ => format!("unknown: {}", format),
521 }
522}
523
524pub fn walk(addr: String, user: String, timeout: Duration, dir: &str) {
525 datadriven::walk(dir, |tf| run_test(tf, addr.clone(), user.clone(), timeout));
526}
527
528pub fn run_test(tf: &mut datadriven::TestFile, addr: String, user: String, timeout: Duration) {
529 let mut pgt = PgTest::new(addr, user, timeout).unwrap();
530 let mut replacements: Vec<(Regex, String)> = Vec::new();
531 tf.run(|tc| -> String {
532 let lines = tc.input.lines();
533 let mut args = tc.args.clone();
534 let conn: Option<String> = args
535 .remove("conn")
536 .map(|args| Some(args.into_first()))
537 .unwrap_or(None);
538 let mut options: Vec<(&str, &str)> = Vec::new();
539 let cluster = args.remove("cluster");
540 if let Some(cluster) = &cluster {
541 let cluster = cluster.into_first();
542 options.push(("cluster", cluster.as_str()));
543 }
544 match tc.directive.as_str() {
545 "send" => {
546 for line in lines {
547 if pgt.verbose {
548 println!("SEND {}", line);
549 }
550 let mut line = line.splitn(2, ' ');
551 let typ = line.next().unwrap_or("");
552 let args = line.next().unwrap_or("{}");
553 pgt.send(conn.clone(), options.clone(), |buf| match typ {
554 "Query" => {
555 let v: Query = serde_json::from_str(args).unwrap();
556 frontend::query(&v.query, buf).unwrap();
557 }
558 "Parse" => {
559 let v: Parse = serde_json::from_str(args).unwrap();
560 frontend::parse(
561 &v.name.unwrap_or_else(|| "".into()),
562 &v.query,
563 vec![],
564 buf,
565 )
566 .unwrap();
567 }
568 "Sync" => frontend::sync(buf),
569 "Bind" => {
570 let v: Bind = serde_json::from_str(args).unwrap();
571 let values: Vec<Vec<u8>> = match v.binary_values {
577 Some(binary_values) => binary_values,
578 None => v
579 .values
580 .unwrap_or_default()
581 .into_iter()
582 .map(String::into_bytes)
583 .collect(),
584 };
585 if frontend::bind(
586 &v.portal.unwrap_or_else(|| "".into()),
587 &v.statement.unwrap_or_else(|| "".into()),
588 v.param_formats.unwrap_or_default(), values, |bytes: Vec<u8>, buf| {
591 buf.put_slice(&bytes);
592 Ok(IsNull::No)
593 }, v.result_formats.unwrap_or_default(),
595 buf,
596 )
597 .is_err()
598 {
599 panic!("bind error");
600 }
601 }
602 "Describe" => {
603 let v: Describe = serde_json::from_str(args).unwrap();
604 frontend::describe(
605 v.variant.unwrap_or_else(|| "S".into()).as_bytes()[0],
606 &v.name.unwrap_or_else(|| "".into()),
607 buf,
608 )
609 .unwrap();
610 }
611 "Execute" => {
612 let v: Execute = serde_json::from_str(args).unwrap();
613 frontend::execute(
614 &v.portal.unwrap_or_else(|| "".into()),
615 v.max_rows.unwrap_or(0),
616 buf,
617 )
618 .unwrap();
619 }
620 "CopyData" => {
621 let v: String = serde_json::from_str(args).unwrap();
622 frontend::CopyData::new(v.as_bytes()).unwrap().write(buf);
623 }
624 "CopyDone" => {
625 frontend::copy_done(buf);
626 }
627 "CopyFail" => {
628 let v: String = serde_json::from_str(args).unwrap();
629 frontend::copy_fail(&v, buf).unwrap();
630 }
631 _ => panic!("unknown message type {}", typ),
632 })
633 .unwrap();
634 }
635 "".to_string()
636 }
637 "until" => {
638 let err_field_typs = if let Some(_) = args.remove("no_error_fields") {
642 vec![]
643 } else {
644 match args.remove("err_field_typs") {
645 Some(typs) => typs.join("").chars().collect(),
646 None => vec!['C', 'S', 'M'],
647 }
648 };
649 let mut ignore = BTreeSet::new();
650 if let Some(values) = args.remove("ignore") {
651 for v in values {
652 ignore.insert(v);
653 }
654 }
655 let parameter_status: BTreeSet<String> = args
656 .remove("parameter_status")
657 .unwrap_or_default()
658 .into_iter()
659 .collect();
660 if !args.is_empty() {
661 panic!("extra until arguments: {:?}", args);
662 }
663 let mut out = pgt
664 .until(
665 conn,
666 options,
667 lines.collect(),
668 err_field_typs,
669 ignore,
670 ¶meter_status,
671 )
672 .unwrap()
673 .join("\n");
674 for (regex, replacement) in &replacements {
675 out = regex.replace_all(&out, replacement.as_str()).into_owned();
676 }
677 format!("{}\n", out)
678 }
679 "replace" => {
680 let [pattern, replacement] = lines.collect::<Vec<_>>()[..] else {
681 panic!(
682 "replace takes two lines, a regex and its replacement: {:?}",
683 tc.input
684 );
685 };
686 replacements.push((Regex::new(pattern).unwrap(), replacement.to_string()));
687 String::new()
688 }
689 _ => panic!("unknown directive {}", tc.input),
690 }
691 })
692}
693
694#[derive(Deserialize)]
697#[serde(deny_unknown_fields)]
698pub struct Query {
699 pub query: String,
700}
701
702#[derive(Deserialize)]
703#[serde(deny_unknown_fields)]
704pub struct Parse {
705 pub name: Option<String>,
706 pub query: String,
707}
708
709#[derive(Deserialize)]
710#[serde(deny_unknown_fields)]
711pub struct Bind {
712 pub portal: Option<String>,
713 pub statement: Option<String>,
714 pub values: Option<Vec<String>>,
715 pub param_formats: Option<Vec<i16>>,
718 pub binary_values: Option<Vec<Vec<u8>>>,
721 pub result_formats: Option<Vec<i16>>,
722}
723
724#[derive(Deserialize)]
725#[serde(deny_unknown_fields)]
726pub struct Execute {
727 pub portal: Option<String>,
728 pub max_rows: Option<i32>,
729}
730
731#[derive(Deserialize)]
732#[serde(deny_unknown_fields)]
733pub struct Describe {
734 pub variant: Option<String>,
735 pub name: Option<String>,
736}