risingwave_frontend/handler/
drop_user.rs

1// Copyright 2025 RisingWave Labs
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 pgwire::pg_response::{PgResponse, StatementType};
16use risingwave_sqlparser::ast::ObjectName;
17
18use super::RwPgResponse;
19use crate::binder::Binder;
20use crate::catalog::CatalogError;
21use crate::error::{ErrorCode, Result};
22use crate::handler::HandlerArgs;
23
24pub async fn handle_drop_user(
25    handler_args: HandlerArgs,
26    user_name: ObjectName,
27    if_exists: bool,
28) -> Result<RwPgResponse> {
29    let session = handler_args.session;
30
31    let user_name = Binder::resolve_user_name(user_name)?;
32    let user_info_reader = session.env().user_info_reader();
33    let user_info = user_info_reader
34        .read_guard()
35        .get_user_by_name(&user_name)
36        .map(|u| (u.id, u.is_super));
37    match user_info {
38        Some((user_id, is_super)) => {
39            if session.user_id() == user_id {
40                return Err(ErrorCode::PermissionDenied(
41                    "current user cannot be dropped".to_owned(),
42                )
43                .into());
44            }
45            if let Some(current_user) = user_info_reader
46                .read_guard()
47                .get_user_by_name(&session.user_name())
48            {
49                if !current_user.is_super {
50                    if is_super {
51                        return Err(ErrorCode::PermissionDenied(
52                            "must be superuser to drop superusers".to_owned(),
53                        )
54                        .into());
55                    }
56                    if !current_user.can_create_user {
57                        return Err(ErrorCode::PermissionDenied(
58                            "permission denied to drop user".to_owned(),
59                        )
60                        .into());
61                    }
62                }
63            } else {
64                return Err(
65                    ErrorCode::PermissionDenied("Session user is invalid".to_owned()).into(),
66                );
67            }
68
69            let user_info_writer = session.user_info_writer()?;
70            user_info_writer.drop_user(user_id).await?;
71        }
72        None => {
73            return if if_exists {
74                Ok(PgResponse::builder(StatementType::DROP_USER)
75                    .notice(format!("user \"{}\" does not exist, skipping", user_name))
76                    .into())
77            } else {
78                Err(CatalogError::NotFound("user", user_name).into())
79            };
80        }
81    }
82
83    Ok(PgResponse::empty_result(StatementType::DROP_USER))
84}
85
86#[cfg(test)]
87mod tests {
88    use crate::test_utils::LocalFrontend;
89
90    #[tokio::test]
91    async fn test_drop_user() {
92        let frontend = LocalFrontend::new(Default::default()).await;
93        let session = frontend.session_ref();
94        let user_info_reader = session.env().user_info_reader();
95
96        frontend.run_sql("CREATE USER user").await.unwrap();
97        assert!(
98            user_info_reader
99                .read_guard()
100                .get_user_by_name("user")
101                .is_some()
102        );
103
104        frontend.run_sql("DROP USER user").await.unwrap();
105        assert!(
106            user_info_reader
107                .read_guard()
108                .get_user_by_name("user")
109                .is_none()
110        );
111    }
112}