1use std::time::Duration;
16
17use anyhow::anyhow;
18use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
19use pg_interval::Interval;
20use rust_decimal::Decimal;
21use tokio::select;
22use tokio_postgres::types::Type;
23use tokio_postgres::{Client, NoTls};
24
25pub struct TestSuite {
26 config: String,
27}
28
29macro_rules! test_eq {
30 ($left:expr, $right:expr $(,)?) => {
31 match (&$left, &$right) {
32 (left_val, right_val) => {
33 if !(*left_val == *right_val) {
34 return Err(anyhow!(
35 "{}:{} assertion failed: `(left == right)` \
36 (left: `{:?}`, right: `{:?}`)",
37 file!(),
38 line!(),
39 left_val,
40 right_val
41 ));
42 }
43 }
44 }
45 };
46}
47
48impl TestSuite {
49 pub fn new(
50 db_name: String,
51 user_name: String,
52 server_host: String,
53 server_port: u16,
54 password: String,
55 ) -> Self {
56 let config = if !password.is_empty() {
57 format!(
58 "dbname={} user={} host={} port={} password={}",
59 db_name, user_name, server_host, server_port, password
60 )
61 } else {
62 format!(
63 "dbname={} user={} host={} port={}",
64 db_name, user_name, server_host, server_port
65 )
66 };
67 Self { config }
68 }
69
70 fn init_logger() {
71 let _ = tracing_subscriber::fmt()
72 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
73 .with_ansi(false)
74 .try_init();
75 }
76
77 pub async fn test(&self) -> anyhow::Result<()> {
78 Self::init_logger();
79 self.binary_param_and_result().await?;
80 self.dql_dml_with_param().await?;
81 self.max_row().await?;
82 self.multiple_on_going_portal().await?;
83 self.create_with_parameter().await?;
84 self.simple_cancel(false).await?;
85 self.simple_cancel(true).await?;
86 self.complex_cancel(false).await?;
87 self.complex_cancel(true).await?;
88 self.subscription_fetch_cancel(false).await?;
89 self.subscription_fetch_cancel(true).await?;
90 self.subquery_with_param().await?;
91 self.create_mview_with_parameter().await?;
92 Ok(())
93 }
94
95 async fn create_client(&self, is_distributed: bool) -> anyhow::Result<Client> {
96 let (client, connection) = tokio_postgres::connect(&self.config, NoTls).await?;
97
98 tokio::spawn(async move {
101 if let Err(e) = connection.await {
102 eprintln!("connection error: {}", e);
103 }
104 });
105
106 if is_distributed {
107 client.execute("set query_mode = distributed", &[]).await?;
108 } else {
109 client.execute("set query_mode = local", &[]).await?;
110 }
111
112 Ok(client)
113 }
114
115 pub async fn binary_param_and_result(&self) -> anyhow::Result<()> {
116 let client = self.create_client(false).await?;
117
118 for row in client.query("select $1::SMALLINT;", &[&1024_i16]).await? {
119 let data: i16 = row.try_get(0)?;
120 test_eq!(data, 1024);
121 }
122
123 for row in client.query("select $1::INT;", &[&144232_i32]).await? {
124 let data: i32 = row.try_get(0)?;
125 test_eq!(data, 144232);
126 }
127
128 for row in client.query("select $1::BIGINT;", &[&99999999_i64]).await? {
129 let data: i64 = row.try_get(0)?;
130 test_eq!(data, 99999999);
131 }
132
133 for row in client
134 .query(
135 "select $1::DECIMAL;",
136 &[&Decimal::try_from(2.33454_f32).ok()],
137 )
138 .await?
139 {
140 let data: Decimal = row.try_get(0)?;
141 test_eq!(data, Decimal::try_from(2.33454_f32).unwrap());
142 }
143
144 for row in client.query("select $1::BOOL;", &[&true]).await? {
145 let data: bool = row.try_get(0)?;
146 assert!(data);
147 }
148
149 for row in client.query("select $1::REAL;", &[&1.234234_f32]).await? {
150 let data: f32 = row.try_get(0)?;
151 test_eq!(data, 1.234234);
152 }
153
154 for row in client
155 .query("select $1::DOUBLE PRECISION;", &[&234234.23490238483_f64])
156 .await?
157 {
158 let data: f64 = row.try_get(0)?;
159 test_eq!(data, 234234.23490238483);
160 }
161
162 for row in client
163 .query(
164 "select $1::date;",
165 &[&NaiveDate::from_ymd_opt(2022, 1, 1).unwrap()],
166 )
167 .await?
168 {
169 let data: NaiveDate = row.try_get(0)?;
170 test_eq!(data, NaiveDate::from_ymd_opt(2022, 1, 1).unwrap());
171 }
172
173 for row in client
174 .query(
175 "select $1::time",
176 &[&NaiveTime::from_hms_opt(10, 0, 0).unwrap()],
177 )
178 .await?
179 {
180 let data: NaiveTime = row.try_get(0)?;
181 test_eq!(data, NaiveTime::from_hms_opt(10, 0, 0).unwrap());
182 }
183
184 for row in client
185 .query(
186 "select $1::timestamp",
187 &[&NaiveDate::from_ymd_opt(2022, 1, 1)
188 .unwrap()
189 .and_hms_opt(10, 0, 0)
190 .unwrap()],
191 )
192 .await?
193 {
194 let data: NaiveDateTime = row.try_get(0)?;
195 test_eq!(
196 data,
197 NaiveDate::from_ymd_opt(2022, 1, 1)
198 .unwrap()
199 .and_hms_opt(10, 0, 0)
200 .unwrap()
201 );
202 }
203
204 let timestamptz = DateTime::<Utc>::from_naive_utc_and_offset(
205 NaiveDate::from_ymd_opt(2022, 1, 1)
206 .unwrap()
207 .and_hms_opt(10, 0, 0)
208 .unwrap(),
209 Utc,
210 );
211 for row in client
212 .query("select $1::timestamptz", &[×tamptz])
213 .await?
214 {
215 let data: DateTime<Utc> = row.try_get(0)?;
216 test_eq!(data, timestamptz);
217 }
218
219 for row in client
220 .query("select $1::interval", &[&Interval::new(1, 1, 24000000)])
221 .await?
222 {
223 let data: Interval = row.try_get(0)?;
224 test_eq!(data, Interval::new(1, 1, 24000000));
225 }
226
227 Ok(())
228 }
229
230 async fn dql_dml_with_param(&self) -> anyhow::Result<()> {
231 let client = self.create_client(false).await?;
232
233 client.query("create table t(id int)", &[]).await?;
234
235 let insert_statement = client
236 .prepare_typed("insert INTO t (id) VALUES ($1)", &[])
237 .await?;
238
239 for i in 0..20 {
240 client.execute(&insert_statement, &[&i]).await?;
241 }
242 client.execute("flush", &[]).await?;
243
244 let update_statement = client
245 .prepare_typed(
246 "update t set id = $1 where id < $2",
247 &[Type::INT4, Type::INT4],
248 )
249 .await?;
250 let query_statement = client
251 .prepare_typed(
252 "select * FROM t where id < $1 order by id ASC",
253 &[Type::INT4],
254 )
255 .await?;
256 let delete_statement = client
257 .prepare_typed("delete FROM t where id < $1", &[Type::INT4])
258 .await?;
259
260 let mut i = 0;
261 for row in client.query(&query_statement, &[&10_i32]).await? {
262 let id: i32 = row.try_get(0)?;
263 test_eq!(id, i);
264 i += 1;
265 }
266 test_eq!(i, 10);
267
268 client
269 .execute(&update_statement, &[&100_i32, &10_i32])
270 .await?;
271 client.execute("flush", &[]).await?;
272
273 let mut i = 0;
274 for _ in client.query(&query_statement, &[&10_i32]).await? {
275 i += 1;
276 }
277 test_eq!(i, 0);
278
279 client.execute(&delete_statement, &[&20_i32]).await?;
280 client.execute("flush", &[]).await?;
281
282 let mut i = 0;
283 for row in client.query(&query_statement, &[&101_i32]).await? {
284 let id: i32 = row.try_get(0)?;
285 test_eq!(id, 100);
286 i += 1;
287 }
288 test_eq!(i, 10);
289
290 client.execute("drop table t", &[]).await?;
291
292 Ok(())
293 }
294
295 async fn max_row(&self) -> anyhow::Result<()> {
296 let mut client = self.create_client(false).await?;
297
298 client.query("create table t(id int)", &[]).await?;
299
300 let insert_statement = client
301 .prepare_typed("insert INTO t (id) VALUES ($1)", &[])
302 .await?;
303
304 for i in 0..10 {
305 client.execute(&insert_statement, &[&i]).await?;
306 }
307 client.execute("flush", &[]).await?;
308
309 let transaction = client.transaction().await?;
310 let statement = transaction
311 .prepare_typed("SELECT * FROM t order by id", &[])
312 .await?;
313 let portal = transaction.bind(&statement, &[]).await?;
314
315 for t in 0..5 {
316 let rows = transaction.query_portal(&portal, 1).await?;
317 test_eq!(rows.len(), 1);
318 let row = rows.first().unwrap();
319 let id: i32 = row.get(0);
320 test_eq!(id, t);
321 }
322
323 let mut i = 5;
324 for row in transaction.query_portal(&portal, 3).await? {
325 let id: i32 = row.get(0);
326 test_eq!(id, i);
327 i += 1;
328 }
329 test_eq!(i, 8);
330
331 for row in transaction.query_portal(&portal, 5).await? {
332 let id: i32 = row.get(0);
333 test_eq!(id, i);
334 i += 1;
335 }
336 test_eq!(i, 10);
337
338 transaction.rollback().await?;
339
340 client.execute("drop table t", &[]).await?;
341
342 Ok(())
343 }
344
345 async fn multiple_on_going_portal(&self) -> anyhow::Result<()> {
346 let mut client = self.create_client(false).await?;
347
348 let transaction = client.transaction().await?;
349 let statement = transaction
350 .prepare_typed("SELECT generate_series(1,5,1)", &[])
351 .await?;
352 let portal_1 = transaction.bind(&statement, &[]).await?;
353 let portal_2 = transaction.bind(&statement, &[]).await?;
354
355 let rows = transaction.query_portal(&portal_1, 1).await?;
356 test_eq!(rows.len(), 1);
357 test_eq!(rows.first().unwrap().get::<usize, i32>(0), 1);
358
359 let rows = transaction.query_portal(&portal_2, 1).await?;
360 test_eq!(rows.len(), 1);
361 test_eq!(rows.first().unwrap().get::<usize, i32>(0), 1);
362
363 let rows = transaction.query_portal(&portal_2, 3).await?;
364 test_eq!(rows.len(), 3);
365 test_eq!(rows.first().unwrap().get::<usize, i32>(0), 2);
366 test_eq!(rows.get(1).unwrap().get::<usize, i32>(0), 3);
367 test_eq!(rows.get(2).unwrap().get::<usize, i32>(0), 4);
368
369 let rows = transaction.query_portal(&portal_1, 1).await?;
370 test_eq!(rows.len(), 1);
371 test_eq!(rows.first().unwrap().get::<usize, i32>(0), 2);
372
373 Ok(())
374 }
375
376 async fn create_with_parameter(&self) -> anyhow::Result<()> {
378 let client = self.create_client(false).await?;
379
380 test_eq!(
381 client
382 .query("create table t as select $1", &[])
383 .await
384 .is_err(),
385 true
386 );
387 test_eq!(
388 client
389 .query("create view v as select $1", &[])
390 .await
391 .is_err(),
392 true
393 );
394
395 Ok(())
396 }
397
398 async fn create_mview_with_parameter(&self) -> anyhow::Result<()> {
399 let client = self.create_client(false).await?;
400
401 let statement = client
402 .prepare_typed(
403 "create materialized view mv as select $1 as x",
404 &[Type::INT4],
405 )
406 .await?;
407
408 client.execute(&statement, &[&42_i32]).await?;
409
410 let rows = client.query("select * from mv", &[]).await?;
411 test_eq!(rows.len(), 1);
412 test_eq!(rows.first().unwrap().get::<usize, i32>(0), 42);
413
414 client
416 .execute("alter materialized view mv rename to mv2", &[])
417 .await?;
418
419 let rows = client.query("select * from mv2", &[]).await?;
420 test_eq!(rows.len(), 1);
421 test_eq!(rows.first().unwrap().get::<usize, i32>(0), 42);
422
423 client.execute("drop materialized view mv2", &[]).await?;
424
425 Ok(())
426 }
427
428 async fn simple_cancel(&self, is_distributed: bool) -> anyhow::Result<()> {
429 let client = self.create_client(is_distributed).await?;
430 client.execute("create table t(id int)", &[]).await?;
431
432 let insert_statement = client
433 .prepare_typed("insert INTO t (id) VALUES ($1)", &[])
434 .await?;
435
436 for i in 0..1000 {
437 client.execute(&insert_statement, &[&i]).await?;
438 }
439
440 client.execute("flush", &[]).await?;
441
442 let cancel_token = client.cancel_token();
443
444 let query_handle = tokio::spawn(async move {
445 client.query("select * from t", &[]).await.unwrap();
446 });
447
448 select! {
449 _ = query_handle => {
450 tracing::error!("Failed to cancel query")
451 },
452 _ = cancel_token.cancel_query(NoTls) => {
453 tracing::trace!("Cancel query successfully")
454 },
455 }
456
457 let new_client = self.create_client(is_distributed).await?;
458
459 let rows = new_client
460 .query("select * from t order by id limit 10", &[])
461 .await?;
462
463 test_eq!(rows.len(), 10);
464 for (expect_id, row) in rows.iter().enumerate() {
465 let id: i32 = row.get(0);
466 test_eq!(id, expect_id as i32);
467 }
468
469 new_client.execute("drop table t", &[]).await?;
470
471 Ok(())
472 }
473
474 async fn complex_cancel(&self, is_distributed: bool) -> anyhow::Result<()> {
475 let client = self.create_client(is_distributed).await?;
476
477 client
478 .execute("create table t1(name varchar, id int)", &[])
479 .await?;
480 client
481 .execute("create table t2(name varchar, id int)", &[])
482 .await?;
483 client
484 .execute("create table t3(name varchar, id int)", &[])
485 .await?;
486
487 let insert_statement = client
488 .prepare_typed("insert INTO t1 (name, id) VALUES ($1, $2)", &[])
489 .await?;
490 let insert_statement2 = client
491 .prepare_typed("insert INTO t2 (name, id) VALUES ($1, $2)", &[])
492 .await?;
493 let insert_statement3 = client
494 .prepare_typed("insert INTO t3 (name, id) VALUES ($1, $2)", &[])
495 .await?;
496 for i in 0..1000 {
497 client
498 .execute(&insert_statement, &[&i.to_string(), &i])
499 .await?;
500 client
501 .execute(&insert_statement2, &[&i.to_string(), &i])
502 .await?;
503 client
504 .execute(&insert_statement3, &[&i.to_string(), &i])
505 .await?;
506 }
507
508 client.execute("flush", &[]).await?;
509
510 client.execute("set query_mode=local", &[]).await?;
511
512 let cancel_token = client.cancel_token();
513
514 let query_sql = "SELECT t1.name, t2.id, t3.name
515 FROM t1
516 INNER JOIN (
517 SELECT id, name
518 FROM t2
519 WHERE id IN (
520 SELECT id
521 FROM t1
522 WHERE name LIKE '%1%'
523 )
524 ) AS t2 ON t1.id = t2.id
525 LEFT JOIN t3 ON t2.name = t3.name
526 WHERE t3.id IN (
527 SELECT MAX(id)
528 FROM t3
529 GROUP BY name
530 )
531 ORDER BY t1.name ASC, t3.id DESC
532 ";
533
534 let query_handle = tokio::spawn(async move {
535 let result = client.query(query_sql, &[]).await;
536 match result {
537 Ok(_) => {
538 tracing::error!("Query should be canceled");
539 }
540 Err(e) => {
541 tracing::error!("Query failed with error: {:?}", e);
542 }
543 };
544 });
545
546 select! {
547 _ = query_handle => {
548 tracing::error!("Failed to cancel query")
549 },
550 _ = cancel_token.cancel_query(NoTls) => {
551 tracing::info!("Cancel query successfully")
552 },
553 }
554
555 let new_client = self.create_client(is_distributed).await?;
556
557 let rows = new_client
558 .query(&format!("{} LIMIT 10", query_sql), &[])
559 .await?;
560 let expect_ans = [
561 (1, 1, 1),
562 (10, 10, 10),
563 (100, 100, 100),
564 (101, 101, 101),
565 (102, 102, 102),
566 (103, 103, 103),
567 (104, 104, 104),
568 (105, 105, 105),
569 (106, 106, 106),
570 (107, 107, 107),
571 ];
572 for (i, row) in rows.iter().enumerate() {
573 test_eq!(
574 row.get::<_, String>(0).parse::<i32>().unwrap(),
575 expect_ans[i].0
576 );
577 test_eq!(row.get::<_, i32>(1), expect_ans[i].1);
578 test_eq!(
579 row.get::<_, String>(2).parse::<i32>().unwrap(),
580 expect_ans[i].2
581 );
582 }
583
584 new_client.execute("drop table t1", &[]).await?;
585 new_client.execute("drop table t2", &[]).await?;
586 new_client.execute("drop table t3", &[]).await?;
587 Ok(())
588 }
589
590 async fn subscription_fetch_cancel(&self, is_distributed: bool) -> anyhow::Result<()> {
591 let client = self.create_client(is_distributed).await?;
592 let suffix = if is_distributed { "dist" } else { "local" };
593 let table_name = format!("sub_cancel_t_{suffix}");
594 let subscription_name = format!("sub_cancel_{suffix}");
595
596 client
597 .execute(&format!("create table {table_name}(v int)"), &[])
598 .await?;
599 client
600 .execute(
601 &format!("create subscription {subscription_name} from {table_name} with(retention = '1D')"),
602 &[],
603 )
604 .await?;
605 client
606 .execute(
607 &format!("declare cur subscription cursor for {subscription_name} since now()"),
608 &[],
609 )
610 .await?;
611
612 let cancel_token = client.cancel_token();
613 let fetch_handle = tokio::spawn(async move {
614 client
615 .query("fetch 1 from cur with (timeout = '60s')", &[])
616 .await
617 });
618
619 tokio::time::sleep(Duration::from_secs(1)).await;
620 cancel_token.cancel_query(NoTls).await?;
621
622 let result = tokio::time::timeout(Duration::from_secs(10), fetch_handle).await??;
623 if result.is_ok() {
624 return Err(anyhow!(
625 "subscription cursor fetch should be cancelled by CancelRequest"
626 ));
627 }
628
629 let cleanup_client = self.create_client(is_distributed).await?;
630 cleanup_client
631 .execute(&format!("drop subscription {subscription_name}"), &[])
632 .await?;
633 cleanup_client
634 .execute(&format!("drop table {table_name}"), &[])
635 .await?;
636 Ok(())
637 }
638
639 async fn subquery_with_param(&self) -> anyhow::Result<()> {
640 let client = self.create_client(false).await?;
641
642 let res = client
643 .query("select (select $1::SMALLINT)", &[&1024_i16])
644 .await
645 .unwrap();
646
647 assert_eq!(res[0].get::<usize, i16>(0), 1024_i16);
648
649 Ok(())
650 }
651}