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