Skip to main content

operator/statement/
comment.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 api::v1::CommentOnExpr;
16use common_error::ext::BoxedError;
17use common_meta::cache_invalidator::Context;
18use common_meta::procedure_executor::ExecutorContext;
19use common_meta::rpc::ddl::{CommentObjectType, CommentOnTask, DdlTask, SubmitDdlTaskRequest};
20use common_query::Output;
21use session::context::QueryContextRef;
22use session::table_name::table_idents_to_full_name;
23use snafu::ResultExt;
24use sql::ast::ObjectNamePartExt;
25use sql::statements::comment::{Comment, CommentObject};
26
27use crate::error::{
28    self, ExecuteDdlSnafu, ExternalSnafu, InvalidSqlSnafu, Result, TableMetadataManagerSnafu,
29};
30use crate::statement::StatementExecutor;
31use crate::utils::to_meta_query_context;
32
33impl StatementExecutor {
34    /// Adds a comment to a database object (table, column, or flow).
35    ///
36    /// # Arguments
37    ///
38    /// * `stmt`: A `Comment` struct containing the object to comment on and the comment text.
39    /// * `query_ctx`: A `QueryContextRef` providing contextual information for the query.
40    ///
41    /// # Returns
42    ///
43    /// A `Result` containing the `Output` of the operation, or an error if the operation fails.
44    pub async fn comment(&self, stmt: Comment, query_ctx: QueryContextRef) -> Result<Output> {
45        let mut comment_on_task = self.create_comment_on_task_from_stmt(stmt, &query_ctx)?;
46        comment_on_task
47            .enrich_object_id(
48                self.table_metadata_manager.table_name_manager(),
49                self.flow_metadata_manager.flow_name_manager(),
50            )
51            .await
52            .context(TableMetadataManagerSnafu)?;
53        let cache_idents = comment_on_task.cache_idents();
54
55        let request = SubmitDdlTaskRequest::new(
56            to_meta_query_context(query_ctx),
57            DdlTask::new_comment_on(comment_on_task),
58        );
59
60        self.procedure_executor
61            .submit_ddl_task(&ExecutorContext::default(), request)
62            .await
63            .context(ExecuteDdlSnafu)?;
64
65        // Invalidates local cache ASAP.
66        self.cache_invalidator
67            .invalidate(&Context::default(), &cache_idents)
68            .await
69            .context(error::InvalidateTableCacheSnafu)?;
70
71        Ok(Output::new_with_affected_rows(0))
72    }
73
74    pub async fn comment_by_expr(
75        &self,
76        expr: CommentOnExpr,
77        query_ctx: QueryContextRef,
78    ) -> Result<Output> {
79        let mut comment_on_task = self.create_comment_on_task_from_expr(expr)?;
80        comment_on_task
81            .enrich_object_id(
82                self.table_metadata_manager.table_name_manager(),
83                self.flow_metadata_manager.flow_name_manager(),
84            )
85            .await
86            .context(TableMetadataManagerSnafu)?;
87        let cache_idents = comment_on_task.cache_idents();
88
89        let request = SubmitDdlTaskRequest::new(
90            to_meta_query_context(query_ctx),
91            DdlTask::new_comment_on(comment_on_task),
92        );
93
94        self.procedure_executor
95            .submit_ddl_task(&ExecutorContext::default(), request)
96            .await
97            .context(ExecuteDdlSnafu)?;
98
99        // Invalidates local cache ASAP.
100        self.cache_invalidator
101            .invalidate(&Context::default(), &cache_idents)
102            .await
103            .context(error::InvalidateTableCacheSnafu)?;
104
105        Ok(Output::new_with_affected_rows(0))
106    }
107
108    fn create_comment_on_task_from_expr(&self, expr: CommentOnExpr) -> Result<CommentOnTask> {
109        let object_type = match expr.object_type {
110            0 => CommentObjectType::Table,
111            1 => CommentObjectType::Column,
112            2 => CommentObjectType::Flow,
113            _ => {
114                return InvalidSqlSnafu {
115                    err_msg: format!(
116                        "Invalid CommentObjectType value: {}. Valid values are: 0 (Table), 1 (Column), 2 (Flow)",
117                        expr.object_type
118                    ),
119                }
120                .fail();
121            }
122        };
123
124        Ok(CommentOnTask {
125            catalog_name: expr.catalog_name,
126            schema_name: expr.schema_name,
127            object_type,
128            object_name: expr.object_name,
129            column_name: if expr.column_name.is_empty() {
130                None
131            } else {
132                Some(expr.column_name)
133            },
134            object_id: None,
135            comment: if expr.comment.is_empty() {
136                None
137            } else {
138                Some(expr.comment)
139            },
140        })
141    }
142
143    fn create_comment_on_task_from_stmt(
144        &self,
145        stmt: Comment,
146        query_ctx: &QueryContextRef,
147    ) -> Result<CommentOnTask> {
148        match stmt.object {
149            CommentObject::Table(table) => {
150                let (catalog_name, schema_name, table_name) =
151                    table_idents_to_full_name(&table, query_ctx)
152                        .map_err(BoxedError::new)
153                        .context(ExternalSnafu)?;
154
155                Ok(CommentOnTask {
156                    catalog_name,
157                    schema_name,
158                    object_type: CommentObjectType::Table,
159                    object_name: table_name,
160                    column_name: None,
161                    object_id: None,
162                    comment: stmt.comment,
163                })
164            }
165            CommentObject::Column { table, column } => {
166                let (catalog_name, schema_name, table_name) =
167                    table_idents_to_full_name(&table, query_ctx)
168                        .map_err(BoxedError::new)
169                        .context(ExternalSnafu)?;
170
171                Ok(CommentOnTask {
172                    catalog_name,
173                    schema_name,
174                    object_type: CommentObjectType::Column,
175                    object_name: table_name,
176                    column_name: Some(column.value),
177                    object_id: None,
178                    comment: stmt.comment,
179                })
180            }
181            CommentObject::Flow(flow_name) => {
182                let (catalog_name, flow_name_str) = match &flow_name.0[..] {
183                    [flow] => (
184                        query_ctx.current_catalog().to_string(),
185                        flow.to_string_unquoted(),
186                    ),
187                    [catalog, flow] => (catalog.to_string_unquoted(), flow.to_string_unquoted()),
188                    _ => {
189                        return InvalidSqlSnafu {
190                            err_msg: format!(
191                                "expect flow name to be <catalog>.<flow_name> or <flow_name>, actual: {flow_name}"
192                            ),
193                        }
194                        .fail();
195                    }
196                };
197
198                Ok(CommentOnTask {
199                    catalog_name,
200                    schema_name: String::new(), // Flow doesn't use schema
201                    object_type: CommentObjectType::Flow,
202                    object_name: flow_name_str,
203                    column_name: None,
204                    object_id: None,
205                    comment: stmt.comment,
206                })
207            }
208        }
209    }
210}