Skip to main content

common_meta/ddl/
drop_view.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
15use async_trait::async_trait;
16use common_event_recorder::Event;
17use common_procedure::error::{FromJsonSnafu, ToJsonSnafu};
18use common_procedure::{
19    Context as ProcedureContext, EventContext, EventTrigger, LockKey, Procedure,
20    Result as ProcedureResult, Status,
21};
22use common_telemetry::info;
23use serde::{Deserialize, Serialize};
24use snafu::{OptionExt, ResultExt, ensure};
25use strum::AsRefStr;
26use table::metadata::{TableId, TableInfo, TableType};
27use table::table_reference::TableReference;
28
29use crate::cache_invalidator::Context;
30use crate::ddl::DdlContext;
31use crate::ddl::event::view::{DROP_VIEW_EVENT_TYPE, ViewDdlEvent};
32use crate::ddl::utils::map_to_procedure_error;
33use crate::error::{self, Result};
34use crate::instruction::CacheIdent;
35use crate::key::table_name::TableNameKey;
36use crate::lock_key::{CatalogLock, SchemaLock, TableLock};
37use crate::metrics;
38use crate::rpc::ddl::DropViewTask;
39
40/// The procedure for dropping a view.
41pub struct DropViewProcedure {
42    /// The context of procedure runtime.
43    pub(crate) context: DdlContext,
44    /// The serializable data.
45    pub(crate) data: DropViewData,
46}
47
48impl DropViewProcedure {
49    pub const TYPE_NAME: &'static str = "metasrv-procedure::DropView";
50
51    pub fn new(task: DropViewTask, context: DdlContext) -> Self {
52        Self {
53            context,
54            data: DropViewData {
55                state: DropViewState::Prepare,
56                task,
57            },
58        }
59    }
60
61    pub fn from_json(json: &str, context: DdlContext) -> ProcedureResult<Self> {
62        let data: DropViewData = serde_json::from_str(json).context(FromJsonSnafu)?;
63
64        Ok(Self { context, data })
65    }
66
67    #[cfg(test)]
68    pub(crate) fn state(&self) -> DropViewState {
69        self.data.state
70    }
71
72    /// Checks whether view exists.
73    /// - Early returns if view not exists and `drop_if_exists` is `true`.
74    /// - Throws an error if view not exists and `drop_if_exists` is `false`.
75    pub(crate) async fn on_prepare(&mut self) -> Result<Status> {
76        let table_ref = self.data.table_ref();
77
78        let exists = self
79            .context
80            .table_metadata_manager
81            .table_name_manager()
82            .exists(TableNameKey::new(
83                table_ref.catalog,
84                table_ref.schema,
85                table_ref.table,
86            ))
87            .await?;
88
89        if !exists && self.data.task.drop_if_exists {
90            return Ok(Status::done());
91        }
92
93        ensure!(
94            exists,
95            error::ViewNotFoundSnafu {
96                view_name: table_ref.to_string(),
97            }
98        );
99
100        self.check_view_metadata().await?;
101        self.data.state = DropViewState::DeleteMetadata;
102
103        Ok(Status::executing(true))
104    }
105
106    async fn check_view_metadata(&mut self) -> Result<()> {
107        let view_id = self.data.view_id();
108        let table_info_value = self
109            .context
110            .table_metadata_manager
111            .table_info_manager()
112            .get(view_id)
113            .await?
114            .with_context(|| error::TableInfoNotFoundSnafu {
115                table: self.data.table_ref().to_string(),
116            })?;
117
118        self.ensure_is_view(&table_info_value.table_info)?;
119        self.ensure_view_info_exists(view_id).await?;
120
121        Ok(())
122    }
123
124    fn ensure_is_view(&self, table_info: &TableInfo) -> Result<()> {
125        ensure!(
126            table_info.table_type == TableType::View,
127            error::InvalidViewInfoSnafu {
128                err_msg: format!("{} is not a view", self.data.table_ref()),
129            }
130        );
131        Ok(())
132    }
133
134    async fn ensure_view_info_exists(&self, view_id: TableId) -> Result<()> {
135        self.context
136            .table_metadata_manager
137            .view_info_manager()
138            .get(view_id)
139            .await?
140            .with_context(|| error::ViewNotFoundSnafu {
141                view_name: self.data.table_ref().to_string(),
142            })?;
143        Ok(())
144    }
145
146    async fn on_delete_metadata(&mut self) -> Result<Status> {
147        let view_id = self.data.view_id();
148        self.context
149            .table_metadata_manager
150            .destroy_view_info(view_id, &self.data.table_ref().into())
151            .await?;
152
153        info!("Deleted view metadata for view {view_id}");
154
155        self.data.state = DropViewState::InvalidateViewCache;
156        Ok(Status::executing(true))
157    }
158
159    async fn on_broadcast(&mut self) -> Result<Status> {
160        let view_id = self.data.view_id();
161        let ctx = Context {
162            subject: Some("Invalidate view cache by dropping view".to_string()),
163        };
164
165        self.context
166            .cache_invalidator
167            .invalidate(
168                &ctx,
169                &[
170                    CacheIdent::TableId(view_id),
171                    CacheIdent::TableName(self.data.table_ref().into()),
172                ],
173            )
174            .await?;
175
176        Ok(Status::done())
177    }
178}
179
180#[async_trait]
181impl Procedure for DropViewProcedure {
182    fn type_name(&self) -> &str {
183        Self::TYPE_NAME
184    }
185
186    async fn execute(&mut self, _ctx: &ProcedureContext) -> ProcedureResult<Status> {
187        let state = &self.data.state;
188        let _timer = metrics::METRIC_META_PROCEDURE_DROP_VIEW
189            .with_label_values(&[state.as_ref()])
190            .start_timer();
191
192        match self.data.state {
193            DropViewState::Prepare => self.on_prepare().await,
194            DropViewState::DeleteMetadata => self.on_delete_metadata().await,
195            DropViewState::InvalidateViewCache => self.on_broadcast().await,
196        }
197        .map_err(map_to_procedure_error)
198    }
199
200    fn dump(&self) -> ProcedureResult<String> {
201        serde_json::to_string(&self.data).context(ToJsonSnafu)
202    }
203
204    fn lock_key(&self) -> LockKey {
205        let table_ref = &self.data.table_ref();
206        let view_id = self.data.view_id();
207        let lock_key = vec![
208            CatalogLock::Read(table_ref.catalog).into(),
209            SchemaLock::read(table_ref.catalog, table_ref.schema).into(),
210            TableLock::Write(view_id).into(),
211        ];
212
213        LockKey::new(lock_key)
214    }
215
216    fn event(&self, ctx: &EventContext<'_>) -> Option<Box<dyn Event>> {
217        if !ctx.event_type_filter.allows(DROP_VIEW_EVENT_TYPE) {
218            return None;
219        }
220
221        let event = match &ctx.trigger {
222            EventTrigger::Submitted => {
223                let table_ref = self.data.table_ref();
224                ViewDdlEvent::drop_submitted(
225                    table_ref.catalog,
226                    table_ref.schema,
227                    table_ref.table,
228                    self.data.view_id(),
229                    self.data.task.drop_if_exists,
230                )
231            }
232            _ => {
233                let table_ref = self.data.table_ref();
234                ViewDdlEvent::drop_lifecycle(
235                    table_ref.catalog,
236                    table_ref.schema,
237                    table_ref.table,
238                    self.data.view_id(),
239                )
240            }
241        };
242
243        Some(Box::new(event))
244    }
245}
246
247/// The serializable data
248#[derive(Debug, Serialize, Deserialize)]
249pub(crate) struct DropViewData {
250    state: DropViewState,
251    task: DropViewTask,
252}
253
254impl DropViewData {
255    fn table_ref(&self) -> TableReference<'_> {
256        self.task.table_ref()
257    }
258
259    fn view_id(&self) -> TableId {
260        self.task.view_id
261    }
262}
263
264/// The state of drop view
265#[derive(Debug, Serialize, Deserialize, AsRefStr, PartialEq, Clone, Copy)]
266pub(crate) enum DropViewState {
267    /// Prepares to drop the view
268    Prepare,
269    /// Deletes metadata
270    DeleteMetadata,
271    /// Invalidate view cache
272    InvalidateViewCache,
273}