Skip to main content

common_meta/ddl/
drop_database.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub 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    /// The context of procedure runtime.
44    runtime_context: DdlContext,
45    context: DropDatabaseContext,
46
47    state: Box<dyn State>,
48}
49
50/// Target of dropping tables.
51#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
52pub(crate) enum DropTableTarget {
53    Logical,
54    Physical,
55}
56
57/// Context of [DropDatabaseProcedure] execution.
58pub(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    /// Yields the next [State] and [Status].
70    async fn next(
71        &mut self,
72        ddl_ctx: &DdlContext,
73        ctx: &mut DropDatabaseContext,
74    ) -> Result<(Box<dyn State>, Status)>;
75
76    /// The hook is called during the recovery.
77    fn recover(&mut self, _ddl_ctx: &DdlContext) -> Result<()> {
78        Ok(())
79    }
80
81    /// Returns as [Any](std::any::Any).
82    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    // The catalog name
198    catalog: &'a str,
199    // The schema name
200    schema: &'a str,
201    drop_if_exists: bool,
202    state: &'a dyn State,
203}
204
205#[derive(Debug, Deserialize)]
206struct DropDatabaseOwnedData {
207    // The catalog name
208    catalog: String,
209    // The schema name
210    schema: String,
211    drop_if_exists: bool,
212    state: Box<dyn State>,
213}