frontend/instance/
import_packed.rs1use auth::{PermissionReq, PermissionTableTarget, PermissionTableTargets};
16use common_query::Output;
17use session::context::QueryContextRef;
18use snafu::ResultExt;
19use sql::statements::copy::CopyDatabaseArgument;
20use sql::statements::statement::Statement;
21use tokio_util::sync::CancellationToken;
22
23use crate::error::{PermissionSnafu, Result};
24use crate::instance::Instance;
25
26impl Instance {
27 pub(crate) async fn copy_packed_database(
28 &self,
29 arg: CopyDatabaseArgument,
30 stmt: &Statement,
31 ctx: QueryContextRef,
32 ) -> Result<Output> {
33 let req = operator::statement::to_copy_database_request(arg, &ctx)?;
34 let plan = self
35 .statement_executor
36 .prepare_packed_import(req, &ctx)
37 .await?;
38 let targets = PermissionTableTargets::resolved(
39 plan.tables
40 .iter()
41 .map(|t| {
42 let info = t.table_info();
43 PermissionTableTarget::new(&info.catalog_name, &info.schema_name, &info.name)
44 })
45 .collect(),
46 );
47 self.check_table_permission(&ctx, PermissionReq::SqlStatement(stmt), targets)
48 .context(PermissionSnafu)?;
49 let executor = self.statement_executor.clone();
50 run_packed_import(move |cancellation| async move {
51 executor.import_packed(plan, &cancellation, ctx).await
52 })
53 .await
54 }
55}
56
57async fn run_packed_import<F, Fut>(worker: F) -> Result<Output>
58where
59 F: FnOnce(CancellationToken) -> Fut,
60 Fut: std::future::Future<Output = operator::error::Result<Output>> + Send + 'static,
61{
62 let cancellation = CancellationToken::new();
63 let _guard = cancellation.clone().drop_guard();
64 common_runtime::spawn_query(worker(cancellation))
67 .await
68 .context(operator::error::JoinTaskSnafu)?
69 .map_err(Into::into)
70}
71
72#[cfg(test)]
73mod tests {
74 use std::time::Duration;
75
76 use tokio::sync::oneshot;
77
78 use super::*;
79
80 #[tokio::test]
81 async fn packed_request_drop_keeps_worker_alive_until_cleanup() {
82 let (started_tx, started_rx) = oneshot::channel();
83 let (cancelled_tx, cancelled_rx) = oneshot::channel();
84 let (drain_tx, drain_rx) = oneshot::channel();
85 let (finished_tx, mut finished_rx) = oneshot::channel();
86 let mut request = Box::pin(run_packed_import(move |cancellation| async move {
87 started_tx.send(()).unwrap();
88 cancellation.cancelled().await;
89 cancelled_tx.send(()).unwrap();
90 drain_rx.await.unwrap();
92 finished_tx.send(()).unwrap();
93 Ok(Output::new_with_affected_rows(1))
94 }));
95 tokio::select! {
96 _ = started_rx => {}
97 _ = &mut request => panic!("worker must wait for cancellation"),
98 }
99 drop(request);
100 tokio::time::timeout(Duration::from_secs(5), cancelled_rx)
101 .await
102 .unwrap()
103 .unwrap();
104 assert!(matches!(
105 finished_rx.try_recv(),
106 Err(oneshot::error::TryRecvError::Empty)
107 ));
108 drain_tx.send(()).unwrap();
109 tokio::time::timeout(Duration::from_secs(5), finished_rx)
110 .await
111 .unwrap()
112 .unwrap();
113 }
114}