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