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