risingwave_frontend/handler/
alter_rename.rs1use pgwire::pg_response::{PgResponse, StatementType};
16use risingwave_common::acl::AclMode;
17use risingwave_common::catalog::is_system_schema;
18use risingwave_sqlparser::ast::ObjectName;
19
20use super::{HandlerArgs, RwPgResponse};
21use crate::Binder;
22use crate::catalog::OwnedByUserCatalog;
23use crate::catalog::root_catalog::SchemaPath;
24use crate::catalog::table_catalog::TableType;
25use crate::error::{ErrorCode, Result};
26
27pub async fn handle_rename_table(
28 handler_args: HandlerArgs,
29 table_type: TableType,
30 table_name: ObjectName,
31 new_table_name: ObjectName,
32) -> Result<RwPgResponse> {
33 let session = handler_args.session;
34 let db_name = &session.database();
35 let (schema_name, real_table_name) =
36 Binder::resolve_schema_qualified_name(db_name, &table_name)?;
37 let new_table_name = Binder::resolve_table_name(new_table_name)?;
38 let search_path = session.config().search_path();
39 let user_name = &session.user_name();
40
41 let schema_path = SchemaPath::new(schema_name.as_deref(), &search_path, user_name);
42
43 let table_id = {
44 let reader = session.env().catalog_reader().read_guard();
45 let (table, schema_name) =
46 reader.get_created_table_by_name(db_name, schema_path, &real_table_name)?;
47 if table_type != table.table_type {
48 return Err(ErrorCode::InvalidInputSyntax(format!(
49 "\"{table_name}\" is not a {}",
50 table_type.to_prost().as_str_name()
51 ))
52 .into());
53 }
54
55 session.check_privilege_for_drop_alter(schema_name, &**table)?;
56 table.id
57 };
58
59 let catalog_writer = session.catalog_writer()?;
60 catalog_writer
61 .alter_name(table_id.into(), &new_table_name)
62 .await?;
63
64 let stmt_type = match table_type {
65 TableType::Table => StatementType::ALTER_TABLE,
66 TableType::MaterializedView => StatementType::ALTER_MATERIALIZED_VIEW,
67 _ => unreachable!(),
68 };
69 Ok(PgResponse::empty_result(stmt_type))
70}
71
72pub async fn handle_rename_index(
73 handler_args: HandlerArgs,
74 index_name: ObjectName,
75 new_index_name: ObjectName,
76) -> Result<RwPgResponse> {
77 let session = handler_args.session;
78 let db_name = &session.database();
79 let (schema_name, real_index_name) =
80 Binder::resolve_schema_qualified_name(db_name, &index_name)?;
81 let new_index_name = Binder::resolve_index_name(new_index_name)?;
82 let search_path = session.config().search_path();
83 let user_name = &session.user_name();
84
85 let schema_path = SchemaPath::new(schema_name.as_deref(), &search_path, user_name);
86
87 let index_id = {
88 let reader = session.env().catalog_reader().read_guard();
89 let (index, schema_name) =
90 reader.get_index_by_name(db_name, schema_path, &real_index_name)?;
91 session.check_privilege_for_drop_alter(schema_name, &**index)?;
92 index.id
93 };
94
95 let catalog_writer = session.catalog_writer()?;
96 catalog_writer
97 .alter_name(index_id.into(), &new_index_name)
98 .await?;
99
100 Ok(PgResponse::empty_result(StatementType::ALTER_INDEX))
101}
102
103pub async fn handle_rename_view(
104 handler_args: HandlerArgs,
105 view_name: ObjectName,
106 new_view_name: ObjectName,
107) -> Result<RwPgResponse> {
108 let session = handler_args.session;
109 let db_name = &session.database();
110 let (schema_name, real_view_name) = Binder::resolve_schema_qualified_name(db_name, &view_name)?;
111 let new_view_name = Binder::resolve_view_name(new_view_name)?;
112 let search_path = session.config().search_path();
113 let user_name = &session.user_name();
114
115 let schema_path = SchemaPath::new(schema_name.as_deref(), &search_path, user_name);
116
117 let view_id = {
118 let reader = session.env().catalog_reader().read_guard();
119 let (view, schema_name) = reader.get_view_by_name(db_name, schema_path, &real_view_name)?;
120 session.check_privilege_for_drop_alter(schema_name, &**view)?;
121 view.id
122 };
123
124 let catalog_writer = session.catalog_writer()?;
125 catalog_writer
126 .alter_name(view_id.into(), &new_view_name)
127 .await?;
128
129 Ok(PgResponse::empty_result(StatementType::ALTER_VIEW))
130}
131
132pub async fn handle_rename_sink(
133 handler_args: HandlerArgs,
134 sink_name: ObjectName,
135 new_sink_name: ObjectName,
136) -> Result<RwPgResponse> {
137 let session = handler_args.session;
138 let db_name = &session.database();
139 let (schema_name, real_sink_name) = Binder::resolve_schema_qualified_name(db_name, &sink_name)?;
140 let new_sink_name = Binder::resolve_sink_name(new_sink_name)?;
141 let search_path = session.config().search_path();
142 let user_name = &session.user_name();
143
144 let schema_path = SchemaPath::new(schema_name.as_deref(), &search_path, user_name);
145
146 let sink_id = {
147 let reader = session.env().catalog_reader().read_guard();
148 let (sink, schema_name) =
149 reader.get_created_sink_by_name(db_name, schema_path, &real_sink_name)?;
150 session.check_privilege_for_drop_alter(schema_name, &**sink)?;
151 sink.id
152 };
153
154 let catalog_writer = session.catalog_writer()?;
155 catalog_writer
156 .alter_name(sink_id.into(), &new_sink_name)
157 .await?;
158
159 Ok(PgResponse::empty_result(StatementType::ALTER_SINK))
160}
161
162pub async fn handle_rename_subscription(
163 handler_args: HandlerArgs,
164 subscription_name: ObjectName,
165 new_subscription_name: ObjectName,
166) -> Result<RwPgResponse> {
167 let session = handler_args.session;
168 let db_name = &session.database();
169 let (schema_name, real_subscription_name) =
170 Binder::resolve_schema_qualified_name(db_name, &subscription_name)?;
171 let new_subscription_name = Binder::resolve_subscription_name(new_subscription_name)?;
172 let search_path = session.config().search_path();
173 let user_name = &session.user_name();
174
175 let schema_path = SchemaPath::new(schema_name.as_deref(), &search_path, user_name);
176
177 let subscription_id = {
178 let reader = session.env().catalog_reader().read_guard();
179 let (subscription, schema_name) =
180 reader.get_subscription_by_name(db_name, schema_path, &real_subscription_name)?;
181 session.check_privilege_for_drop_alter(schema_name, &**subscription)?;
182 subscription.id
183 };
184
185 let catalog_writer = session.catalog_writer()?;
186 catalog_writer
187 .alter_name(subscription_id.into(), &new_subscription_name)
188 .await?;
189
190 Ok(PgResponse::empty_result(StatementType::ALTER_SUBSCRIPTION))
191}
192
193pub async fn handle_rename_source(
194 handler_args: HandlerArgs,
195 source_name: ObjectName,
196 new_source_name: ObjectName,
197) -> Result<RwPgResponse> {
198 let session = handler_args.session;
199 let db_name = &session.database();
200 let (schema_name, real_source_name) =
201 Binder::resolve_schema_qualified_name(db_name, &source_name)?;
202 let new_source_name = Binder::resolve_source_name(new_source_name)?;
203 let search_path = session.config().search_path();
204 let user_name = &session.user_name();
205
206 let schema_path = SchemaPath::new(schema_name.as_deref(), &search_path, user_name);
207
208 let source_id = {
209 let reader = session.env().catalog_reader().read_guard();
210 let (source, schema_name) =
211 reader.get_source_by_name(db_name, schema_path, &real_source_name)?;
212
213 if source.associated_table_id.is_some() {
215 return Err(ErrorCode::InvalidInputSyntax(
216 "Use `ALTER TABLE` to alter a table with connector.".to_owned(),
217 )
218 .into());
219 }
220
221 session.check_privilege_for_drop_alter(schema_name, &**source)?;
222 source.id
223 };
224
225 let catalog_writer = session.catalog_writer()?;
226 catalog_writer
227 .alter_name(source_id.into(), &new_source_name)
228 .await?;
229
230 Ok(PgResponse::empty_result(StatementType::ALTER_SOURCE))
231}
232
233pub async fn handle_rename_schema(
234 handler_args: HandlerArgs,
235 schema_name: ObjectName,
236 new_schema_name: ObjectName,
237) -> Result<RwPgResponse> {
238 let session = handler_args.session;
239 let db_name = &session.database();
240 let schema_name = Binder::resolve_schema_name(schema_name)?;
241 let new_schema_name = Binder::resolve_schema_name(new_schema_name)?;
242
243 let schema_id = {
244 let catalog_reader = session.env().catalog_reader().read_guard();
245 let user_reader = session.env().user_info_reader().read_guard();
246 let schema = catalog_reader.get_schema_by_name(db_name, &schema_name)?;
247 let db_id = catalog_reader.get_database_by_name(db_name)?.id();
248
249 if is_system_schema(&schema.name()) {
251 return Err(ErrorCode::ProtocolError(format!(
252 "permission denied to rename on \"{}\", System catalog modifications are currently disallowed.",
253 schema_name
254 )).into());
255 }
256
257 session.check_privilege_for_drop_alter_db_schema(schema)?;
259
260 if let Some(user) = user_reader.get_user_by_name(&session.user_name()) {
262 if !user.is_super && !user.has_privilege(db_id, AclMode::Create) {
263 return Err(ErrorCode::PermissionDenied(
264 "Do not have CREATE privilege on the current database".to_owned(),
265 )
266 .into());
267 }
268 } else {
269 return Err(ErrorCode::PermissionDenied("Session user is invalid".to_owned()).into());
270 }
271
272 schema.id()
273 };
274
275 let catalog_writer = session.catalog_writer()?;
276 catalog_writer
277 .alter_name(schema_id.into(), &new_schema_name)
278 .await?;
279
280 Ok(PgResponse::empty_result(StatementType::ALTER_SCHEMA))
281}
282
283pub async fn handle_rename_database(
284 handler_args: HandlerArgs,
285 database_name: ObjectName,
286 new_database_name: ObjectName,
287) -> Result<RwPgResponse> {
288 let session = handler_args.session;
289 let database_name = Binder::resolve_database_name(database_name)?;
290 let new_database_name = Binder::resolve_database_name(new_database_name)?;
291
292 let database_id = {
293 let catalog_reader = session.env().catalog_reader().read_guard();
294 let user_reader = session.env().user_info_reader().read_guard();
295 let database = catalog_reader.get_database_by_name(&database_name)?;
296 let current_user = user_reader
297 .get_user_by_name(&session.user_name())
298 .ok_or_else(|| ErrorCode::PermissionDenied("Session user is invalid".to_owned()))?;
299
300 if let Some(database_owner) = user_reader.get_user_by_id(&database.owner())
302 && database_owner.is_admin
303 && !current_user.is_admin
304 {
305 return Err(ErrorCode::PermissionDenied(
306 "only admin users can rename databases owned by admin users".to_owned(),
307 )
308 .into());
309 }
310
311 session.check_privilege_for_drop_alter_db_schema(database)?;
313
314 if !current_user.is_super && !current_user.can_create_db {
316 return Err(ErrorCode::PermissionDenied(
317 "Non-superuser owners must also have the CREATEDB privilege".to_owned(),
318 )
319 .into());
320 }
321
322 if database_name == session.database() {
324 return Err(ErrorCode::PermissionDenied(
325 "Current database cannot be renamed".to_owned(),
326 )
327 .into());
328 }
329
330 database.id()
331 };
332
333 let catalog_writer = session.catalog_writer()?;
334 catalog_writer
335 .alter_name(database_id.into(), &new_database_name)
336 .await?;
337
338 Ok(PgResponse::empty_result(StatementType::ALTER_DATABASE))
339}
340
341#[cfg(test)]
342mod tests {
343 use risingwave_common::catalog::{
344 DEFAULT_DATABASE_NAME, DEFAULT_SCHEMA_NAME, DEFAULT_SUPER_USER_FOR_ADMIN,
345 DEFAULT_SUPER_USER_FOR_ADMIN_ID,
346 };
347
348 use crate::catalog::root_catalog::SchemaPath;
349 use crate::test_utils::LocalFrontend;
350
351 #[tokio::test]
352 async fn test_alter_table_name_handler() {
353 let frontend = LocalFrontend::new(Default::default()).await;
354 let session = frontend.session_ref();
355 let schema_path = SchemaPath::Name(DEFAULT_SCHEMA_NAME);
356
357 let sql = "create table t (i int, r real);";
358 frontend.run_sql(sql).await.unwrap();
359
360 let table_id = {
361 let catalog_reader = session.env().catalog_reader().read_guard();
362 catalog_reader
363 .get_created_table_by_name(DEFAULT_DATABASE_NAME, schema_path, "t")
364 .unwrap()
365 .0
366 .id
367 };
368
369 let sql = "alter table t rename to t1;";
371 frontend.run_sql(sql).await.unwrap();
372
373 let catalog_reader = session.env().catalog_reader().read_guard();
374 let altered_table_name = catalog_reader
375 .get_any_table_by_id(table_id)
376 .unwrap()
377 .name()
378 .to_owned();
379 assert_eq!(altered_table_name, "t1");
380 }
381
382 #[tokio::test]
383 async fn test_rename_admin_owned_database() {
384 let frontend = LocalFrontend::new(Default::default()).await;
385 let session = frontend.session_ref();
386 let catalog_reader = session.env().catalog_reader();
387
388 frontend
389 .run_user_sql(
390 "CREATE DATABASE admin_owned_database",
391 DEFAULT_DATABASE_NAME.to_owned(),
392 DEFAULT_SUPER_USER_FOR_ADMIN.to_owned(),
393 DEFAULT_SUPER_USER_FOR_ADMIN_ID,
394 )
395 .await
396 .unwrap();
397
398 let err = frontend
399 .run_sql("ALTER DATABASE admin_owned_database RENAME TO renamed_database")
400 .await
401 .unwrap_err();
402 assert!(
403 err.to_string()
404 .contains("only admin users can rename databases owned by admin users")
405 );
406 assert!(
407 catalog_reader
408 .read_guard()
409 .get_database_by_name("admin_owned_database")
410 .is_ok()
411 );
412
413 frontend
414 .run_user_sql(
415 "ALTER DATABASE admin_owned_database RENAME TO renamed_database",
416 DEFAULT_DATABASE_NAME.to_owned(),
417 DEFAULT_SUPER_USER_FOR_ADMIN.to_owned(),
418 DEFAULT_SUPER_USER_FOR_ADMIN_ID,
419 )
420 .await
421 .unwrap();
422 assert!(
423 catalog_reader
424 .read_guard()
425 .get_database_by_name("renamed_database")
426 .is_ok()
427 );
428
429 frontend
430 .run_sql("CREATE DATABASE regular_database")
431 .await
432 .unwrap();
433 frontend
434 .run_sql("ALTER DATABASE regular_database RENAME TO renamed_regular_database")
435 .await
436 .unwrap();
437 assert!(
438 catalog_reader
439 .read_guard()
440 .get_database_by_name("renamed_regular_database")
441 .is_ok()
442 );
443 }
444}