1pub mod cursor;
16pub mod end;
17pub mod executor;
18pub mod metadata;
19pub mod start;
20use std::any::Any;
21use std::fmt::Debug;
22
23use common_error::ext::BoxedError;
24use common_procedure::error::{ExternalSnafu, FromJsonSnafu, ToJsonSnafu};
25use common_procedure::{
26 Context as ProcedureContext, EventContext, EventTrigger, LockKey, Procedure,
27 Result as ProcedureResult, Status,
28};
29use futures::stream::BoxStream;
30use serde::{Deserialize, Serialize};
31use snafu::ResultExt;
32use tonic::async_trait;
33
34use self::start::DropDatabaseStart;
35use crate::ddl::DdlContext;
36use crate::ddl::event::database::{DROP_DATABASE_EVENT_TYPE, DatabaseDdlEvent};
37use crate::ddl::utils::map_to_procedure_error;
38use crate::error::Result;
39use crate::key::table_name::TableNameValue;
40use crate::lock_key::{CatalogLock, SchemaLock};
41
42pub struct DropDatabaseProcedure {
43 runtime_context: DdlContext,
45 context: DropDatabaseContext,
46
47 state: Box<dyn State>,
48}
49
50#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
52pub(crate) enum DropTableTarget {
53 Logical,
54 Physical,
55}
56
57pub(crate) struct DropDatabaseContext {
59 catalog: String,
60 schema: String,
61 drop_if_exists: bool,
62 tables: Option<BoxStream<'static, Result<(String, TableNameValue)>>>,
63 retrying: bool,
64}
65
66#[async_trait::async_trait]
67#[typetag::serde(tag = "drop_database_state")]
68pub(crate) trait State: Send + Debug {
69 async fn next(
71 &mut self,
72 ddl_ctx: &DdlContext,
73 ctx: &mut DropDatabaseContext,
74 ) -> Result<(Box<dyn State>, Status)>;
75
76 fn recover(&mut self, _ddl_ctx: &DdlContext) -> Result<()> {
78 Ok(())
79 }
80
81 fn as_any(&self) -> &dyn Any;
83}
84
85impl DropDatabaseProcedure {
86 pub const TYPE_NAME: &'static str = "metasrv-procedure::DropDatabase";
87
88 pub fn new(catalog: String, schema: String, drop_if_exists: bool, context: DdlContext) -> Self {
89 Self {
90 runtime_context: context,
91 context: DropDatabaseContext {
92 catalog,
93 schema,
94 drop_if_exists,
95 tables: None,
96 retrying: false,
97 },
98 state: Box::new(DropDatabaseStart),
99 }
100 }
101
102 pub fn from_json(json: &str, runtime_context: DdlContext) -> ProcedureResult<Self> {
103 let DropDatabaseOwnedData {
104 catalog,
105 schema,
106 drop_if_exists,
107 state,
108 } = serde_json::from_str(json).context(FromJsonSnafu)?;
109
110 Ok(Self {
111 runtime_context,
112 context: DropDatabaseContext {
113 catalog,
114 schema,
115 drop_if_exists,
116 tables: None,
117 retrying: false,
118 },
119 state,
120 })
121 }
122
123 #[cfg(test)]
124 pub(crate) fn state(&self) -> &dyn State {
125 self.state.as_ref()
126 }
127}
128
129#[async_trait]
130impl Procedure for DropDatabaseProcedure {
131 fn type_name(&self) -> &str {
132 Self::TYPE_NAME
133 }
134
135 fn recover(&mut self) -> ProcedureResult<()> {
136 self.state
137 .recover(&self.runtime_context)
138 .map_err(BoxedError::new)
139 .context(ExternalSnafu {
140 clean_poisons: false,
141 })
142 }
143
144 async fn execute(&mut self, ctx: &ProcedureContext) -> ProcedureResult<Status> {
145 let state = &mut self.state;
146
147 self.context.retrying = ctx.is_retrying().await.unwrap_or(false);
148 let (next, status) = state
149 .next(&self.runtime_context, &mut self.context)
150 .await
151 .map_err(map_to_procedure_error)?;
152
153 *state = next;
154 Ok(status)
155 }
156
157 fn dump(&self) -> ProcedureResult<String> {
158 let data = DropDatabaseData {
159 catalog: &self.context.catalog,
160 schema: &self.context.schema,
161 drop_if_exists: self.context.drop_if_exists,
162 state: self.state.as_ref(),
163 };
164
165 serde_json::to_string(&data).context(ToJsonSnafu)
166 }
167
168 fn lock_key(&self) -> LockKey {
169 let lock_key = vec![
170 CatalogLock::Read(&self.context.catalog).into(),
171 SchemaLock::write(&self.context.catalog, &self.context.schema).into(),
172 ];
173
174 LockKey::new(lock_key)
175 }
176
177 fn event(&self, ctx: &EventContext<'_>) -> Option<Box<dyn common_event_recorder::Event>> {
178 if !ctx.event_type_filter.allows(DROP_DATABASE_EVENT_TYPE) {
179 return None;
180 }
181
182 let event = if matches!(&ctx.trigger, EventTrigger::Submitted) {
183 DatabaseDdlEvent::drop_submitted(
184 &self.context.catalog,
185 &self.context.schema,
186 self.context.drop_if_exists,
187 )
188 } else {
189 DatabaseDdlEvent::drop_lifecycle(&self.context.catalog, &self.context.schema)
190 };
191 Some(Box::new(event))
192 }
193}
194
195#[derive(Debug, Serialize)]
196struct DropDatabaseData<'a> {
197 catalog: &'a str,
199 schema: &'a str,
201 drop_if_exists: bool,
202 state: &'a dyn State,
203}
204
205#[derive(Debug, Deserialize)]
206struct DropDatabaseOwnedData {
207 catalog: String,
209 schema: String,
211 drop_if_exists: bool,
212 state: Box<dyn State>,
213}