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