1use std::collections::BTreeSet;
16
17use itertools::Itertools;
18use mz_sql_parser::ast::display::AstDisplay;
19
20use crate::ast::{Ident, UnresolvedDatabaseName};
21use crate::catalog::{
22 DefaultPrivilegeAclItem, DefaultPrivilegeObject, ErrorMessageObjectDescription, ObjectType,
23 SystemObjectType,
24};
25use crate::names::{
26 Aug, ObjectId, ResolvedDatabaseSpecifier, ResolvedRoleName, SchemaSpecifier, SystemObjectId,
27};
28use crate::plan::error::PlanError;
29use crate::plan::statement::ddl::{
30 resolve_cluster, resolve_database, resolve_item_or_type, resolve_network_policy, resolve_schema,
31};
32use crate::plan::statement::{StatementContext, StatementDesc};
33use crate::plan::{
34 AlterDefaultPrivilegesPlan, AlterNoopPlan, AlterOwnerPlan, GrantPrivilegesPlan, GrantRolePlan,
35 Plan, PlanNotice, ReassignOwnedPlan, RevokePrivilegesPlan, RevokeRolePlan, UpdatePrivilege,
36};
37use crate::session::user::SYSTEM_USER;
38use mz_ore::str::StrExt;
39use mz_repr::adt::mz_acl_item::AclMode;
40use mz_repr::role_id::RoleId;
41use mz_sql_parser::ast::{
42 AbbreviatedGrantOrRevokeStatement, AlterDefaultPrivilegesStatement, AlterOwnerStatement,
43 GrantPrivilegesStatement, GrantRoleStatement, GrantTargetAllSpecification,
44 GrantTargetSpecification, GrantTargetSpecificationInner, Privilege, PrivilegeSpecification,
45 ReassignOwnedStatement, RevokePrivilegesStatement, RevokeRoleStatement,
46 TargetRoleSpecification, UnresolvedItemName, UnresolvedObjectName, UnresolvedSchemaName,
47};
48
49pub fn describe_alter_owner(
50 _: &StatementContext,
51 _: AlterOwnerStatement<Aug>,
52) -> Result<StatementDesc, PlanError> {
53 Ok(StatementDesc::new(None))
54}
55
56pub fn plan_alter_owner(
57 scx: &StatementContext,
58 AlterOwnerStatement {
59 object_type,
60 if_exists,
61 name,
62 new_owner,
63 }: AlterOwnerStatement<Aug>,
64) -> Result<Plan, PlanError> {
65 let object_type = object_type.into();
66 match (object_type, name) {
67 (ObjectType::Cluster, UnresolvedObjectName::Cluster(name)) => {
68 plan_alter_cluster_owner(scx, if_exists, name, new_owner.id)
69 }
70 (ObjectType::ClusterReplica, UnresolvedObjectName::ClusterReplica(_)) => {
71 bail_never_supported!("altering the owner of a cluster replica");
72 }
73 (ObjectType::Database, UnresolvedObjectName::Database(name)) => {
74 plan_alter_database_owner(scx, if_exists, name, new_owner.id)
75 }
76 (ObjectType::Schema, UnresolvedObjectName::Schema(name)) => {
77 plan_alter_schema_owner(scx, if_exists, name, new_owner.id)
78 }
79 (ObjectType::NetworkPolicy, UnresolvedObjectName::NetworkPolicy(name)) => {
80 plan_alter_network_policy_owner(scx, if_exists, name, new_owner.id)
81 }
82 (ObjectType::Role, UnresolvedObjectName::Role(_)) => {
84 bail_internal!("cannot ALTER OWNER of a role")
85 }
86 (
87 object_type @ ObjectType::Cluster
88 | object_type @ ObjectType::ClusterReplica
89 | object_type @ ObjectType::Database
90 | object_type @ ObjectType::Schema
91 | object_type @ ObjectType::Role,
92 name,
93 )
94 | (
95 object_type,
96 name @ UnresolvedObjectName::Cluster(_)
97 | name @ UnresolvedObjectName::ClusterReplica(_)
98 | name @ UnresolvedObjectName::Database(_)
99 | name @ UnresolvedObjectName::Schema(_)
100 | name @ UnresolvedObjectName::NetworkPolicy(_)
101 | name @ UnresolvedObjectName::Role(_),
102 ) => {
103 bail_internal!("invalid object type '{object_type}' for ALTER OWNER with name {name}")
105 }
106 (object_type, UnresolvedObjectName::Item(name)) => {
107 plan_alter_item_owner(scx, object_type, if_exists, name, new_owner.id)
108 }
109 }
110}
111
112fn plan_alter_cluster_owner(
113 scx: &StatementContext,
114 if_exists: bool,
115 name: Ident,
116 new_owner: RoleId,
117) -> Result<Plan, PlanError> {
118 match resolve_cluster(scx, &name, if_exists)? {
119 Some(cluster) => Ok(Plan::AlterOwner(AlterOwnerPlan {
120 id: ObjectId::Cluster(cluster.id()),
121 object_type: ObjectType::Cluster,
122 new_owner,
123 })),
124 None => {
125 scx.catalog.add_notice(PlanNotice::ObjectDoesNotExist {
126 name: name.to_ast_string_simple(),
127 object_type: ObjectType::Cluster,
128 });
129 Ok(Plan::AlterNoop(AlterNoopPlan {
130 object_type: ObjectType::Cluster,
131 }))
132 }
133 }
134}
135
136fn plan_alter_database_owner(
137 scx: &StatementContext,
138 if_exists: bool,
139 name: UnresolvedDatabaseName,
140 new_owner: RoleId,
141) -> Result<Plan, PlanError> {
142 match resolve_database(scx, &name, if_exists)? {
143 Some(database) => Ok(Plan::AlterOwner(AlterOwnerPlan {
144 id: ObjectId::Database(database.id()),
145 object_type: ObjectType::Database,
146 new_owner,
147 })),
148 None => {
149 scx.catalog.add_notice(PlanNotice::ObjectDoesNotExist {
150 name: name.to_ast_string_simple(),
151 object_type: ObjectType::Database,
152 });
153
154 Ok(Plan::AlterNoop(AlterNoopPlan {
155 object_type: ObjectType::Database,
156 }))
157 }
158 }
159}
160
161fn plan_alter_schema_owner(
162 scx: &StatementContext,
163 if_exists: bool,
164 name: UnresolvedSchemaName,
165 new_owner: RoleId,
166) -> Result<Plan, PlanError> {
167 let normalized = crate::normalize::unresolved_schema_name(name.clone())?;
171 if normalized.database.is_none() && normalized.schema == mz_repr::namespaces::MZ_TEMP_SCHEMA {
172 sql_bail!("cannot alter schema {name} because it is a temporary schema",)
173 }
174
175 match resolve_schema(scx, name.clone(), if_exists)? {
176 Some((database_spec, schema_spec)) => {
177 if let ResolvedDatabaseSpecifier::Ambient = database_spec {
178 sql_bail!(
179 "cannot alter schema {name} because it is required by the database system",
180 );
181 }
182 if let SchemaSpecifier::Temporary = schema_spec {
183 sql_bail!("cannot alter schema {name} because it is a temporary schema",)
184 }
185 Ok(Plan::AlterOwner(AlterOwnerPlan {
186 id: ObjectId::Schema((database_spec, schema_spec)),
187 object_type: ObjectType::Schema,
188 new_owner,
189 }))
190 }
191 None => {
192 scx.catalog.add_notice(PlanNotice::ObjectDoesNotExist {
193 name: name.to_ast_string_simple(),
194 object_type: ObjectType::Schema,
195 });
196
197 Ok(Plan::AlterNoop(AlterNoopPlan {
198 object_type: ObjectType::Schema,
199 }))
200 }
201 }
202}
203
204fn plan_alter_item_owner(
205 scx: &StatementContext,
206 object_type: ObjectType,
207 if_exists: bool,
208 name: UnresolvedItemName,
209 new_owner: RoleId,
210) -> Result<Plan, PlanError> {
211 let resolved = match resolve_item_or_type(scx, object_type, name.clone(), if_exists) {
212 Ok(r) => r,
213 Err(PlanError::MismatchedObjectType {
215 name,
216 is_type: ObjectType::MaterializedView,
217 expected_type: ObjectType::View,
218 }) => {
219 return Err(PlanError::AlterViewOnMaterializedView(name.to_string()));
220 }
221 e => e?,
222 };
223
224 match resolved {
225 Some(item) => {
226 if item.id().is_system() {
227 sql_bail!(
228 "cannot alter item {} because it is required by the database system",
229 scx.catalog.resolve_full_name(item.name()),
230 );
231 }
232
233 Ok(Plan::AlterOwner(AlterOwnerPlan {
234 id: ObjectId::Item(item.id()),
235 object_type,
236 new_owner,
237 }))
238 }
239 None => {
240 scx.catalog.add_notice(PlanNotice::ObjectDoesNotExist {
241 name: name.to_ast_string_simple(),
242 object_type,
243 });
244
245 Ok(Plan::AlterNoop(AlterNoopPlan { object_type }))
246 }
247 }
248}
249
250fn plan_alter_network_policy_owner(
251 scx: &StatementContext,
252 if_exists: bool,
253 name: Ident,
254 new_owner: RoleId,
255) -> Result<Plan, PlanError> {
256 match resolve_network_policy(scx, name.clone(), if_exists)? {
257 Some(policy_id) => Ok(Plan::AlterOwner(AlterOwnerPlan {
258 id: ObjectId::NetworkPolicy(policy_id.id),
259 object_type: ObjectType::NetworkPolicy,
260 new_owner,
261 })),
262 None => {
263 scx.catalog.add_notice(PlanNotice::ObjectDoesNotExist {
264 name: name.to_ast_string_simple(),
265 object_type: ObjectType::NetworkPolicy,
266 });
267
268 Ok(Plan::AlterNoop(AlterNoopPlan {
269 object_type: ObjectType::NetworkPolicy,
270 }))
271 }
272 }
273}
274
275pub fn describe_grant_role(
276 _: &StatementContext,
277 _: GrantRoleStatement<Aug>,
278) -> Result<StatementDesc, PlanError> {
279 Ok(StatementDesc::new(None))
280}
281
282pub fn plan_grant_role(
283 scx: &StatementContext,
284 GrantRoleStatement {
285 role_names,
286 member_names,
287 }: GrantRoleStatement<Aug>,
288) -> Result<Plan, PlanError> {
289 let grantor_id = scx
295 .catalog
296 .resolve_role(&SYSTEM_USER.name)
297 .expect("system user must exist")
298 .id();
299 Ok(Plan::GrantRole(GrantRolePlan {
300 role_ids: role_names
301 .into_iter()
302 .map(|role_name| role_name.id)
303 .collect(),
304 member_ids: member_names
305 .into_iter()
306 .map(|member_name| member_name.id)
307 .collect(),
308 grantor_id,
309 }))
310}
311
312pub fn describe_revoke_role(
313 _: &StatementContext,
314 _: RevokeRoleStatement<Aug>,
315) -> Result<StatementDesc, PlanError> {
316 Ok(StatementDesc::new(None))
317}
318
319pub fn plan_revoke_role(
320 scx: &StatementContext,
321 RevokeRoleStatement {
322 role_names,
323 member_names,
324 }: RevokeRoleStatement<Aug>,
325) -> Result<Plan, PlanError> {
326 let grantor_id = scx
334 .catalog
335 .resolve_role(&SYSTEM_USER.name)
336 .expect("system user must exist")
337 .id();
338 Ok(Plan::RevokeRole(RevokeRolePlan {
339 role_ids: role_names
340 .into_iter()
341 .map(|role_name| role_name.id)
342 .collect(),
343 member_ids: member_names
344 .into_iter()
345 .map(|member_name| member_name.id)
346 .collect(),
347 grantor_id,
348 }))
349}
350
351pub fn describe_grant_privileges(
352 _: &StatementContext,
353 _: GrantPrivilegesStatement<Aug>,
354) -> Result<StatementDesc, PlanError> {
355 Ok(StatementDesc::new(None))
356}
357
358pub fn plan_grant_privileges(
359 scx: &StatementContext,
360 GrantPrivilegesStatement {
361 privileges,
362 target,
363 roles,
364 }: GrantPrivilegesStatement<Aug>,
365) -> Result<Plan, PlanError> {
366 let plan = plan_update_privilege(scx, privileges, target, roles)?;
367 Ok(Plan::GrantPrivileges(plan.into()))
368}
369
370pub fn describe_revoke_privileges(
371 _: &StatementContext,
372 _: RevokePrivilegesStatement<Aug>,
373) -> Result<StatementDesc, PlanError> {
374 Ok(StatementDesc::new(None))
375}
376
377pub fn plan_revoke_privileges(
378 scx: &StatementContext,
379 RevokePrivilegesStatement {
380 privileges,
381 target,
382 roles,
383 }: RevokePrivilegesStatement<Aug>,
384) -> Result<Plan, PlanError> {
385 let plan = plan_update_privilege(scx, privileges, target, roles)?;
386 Ok(Plan::RevokePrivileges(plan.into()))
387}
388
389struct UpdatePrivilegesPlan {
390 update_privileges: Vec<UpdatePrivilege>,
391 grantees: Vec<RoleId>,
392}
393
394impl From<UpdatePrivilegesPlan> for GrantPrivilegesPlan {
395 fn from(
396 UpdatePrivilegesPlan {
397 update_privileges,
398 grantees,
399 }: UpdatePrivilegesPlan,
400 ) -> GrantPrivilegesPlan {
401 GrantPrivilegesPlan {
402 update_privileges,
403 grantees,
404 }
405 }
406}
407
408impl From<UpdatePrivilegesPlan> for RevokePrivilegesPlan {
409 fn from(
410 UpdatePrivilegesPlan {
411 update_privileges,
412 grantees,
413 }: UpdatePrivilegesPlan,
414 ) -> RevokePrivilegesPlan {
415 RevokePrivilegesPlan {
416 update_privileges,
417 revokees: grantees,
418 }
419 }
420}
421
422fn plan_update_privilege(
423 scx: &StatementContext,
424 privileges: PrivilegeSpecification,
425 target: GrantTargetSpecification<Aug>,
426 roles: Vec<ResolvedRoleName>,
427) -> Result<UpdatePrivilegesPlan, PlanError> {
428 let (object_type, target_ids) = match target {
429 GrantTargetSpecification::Object {
430 object_type,
431 object_spec_inner,
432 } => {
433 fn object_type_filter(
434 object_id: &ObjectId,
435 object_type: &ObjectType,
436 scx: &StatementContext,
437 ) -> bool {
438 if object_type == &ObjectType::Table {
439 scx.get_object_type(object_id).is_relation()
440 } else {
441 object_type == &scx.get_object_type(object_id)
442 }
443 }
444 let object_type = object_type.into();
445 let object_ids: Vec<ObjectId> = match object_spec_inner {
446 GrantTargetSpecificationInner::All(GrantTargetAllSpecification::All) => {
447 let cluster_ids = scx
448 .catalog
449 .get_clusters()
450 .into_iter()
451 .map(|cluster| cluster.id().into());
452 let database_ids = scx
453 .catalog
454 .get_databases()
455 .into_iter()
456 .map(|database| database.id().into());
457 let schema_ids = scx
458 .catalog
459 .get_schemas()
460 .into_iter()
461 .filter(|schema| !schema.id().is_temporary())
462 .map(|schema| (schema.database().clone(), schema.id().clone()).into());
463 let item_ids = scx
464 .catalog
465 .get_items()
466 .into_iter()
467 .map(|item| item.id().into());
468 let network_policy_ids = scx
469 .catalog
470 .get_network_policies()
471 .into_iter()
472 .map(|network_policy| ObjectId::NetworkPolicy(network_policy.id()));
473 cluster_ids
474 .chain(database_ids)
475 .chain(schema_ids)
476 .chain(item_ids)
477 .chain(network_policy_ids)
478 .filter(|object_id| object_type_filter(object_id, &object_type, scx))
479 .filter(|object_id| object_id.is_user())
480 .collect()
481 }
482 GrantTargetSpecificationInner::All(GrantTargetAllSpecification::AllDatabases {
483 databases,
484 }) => {
485 let schema_ids = databases
486 .iter()
487 .map(|database| scx.get_database(database.database_id()))
488 .flat_map(|database| database.schemas().into_iter())
489 .filter(|schema| !schema.id().is_temporary())
490 .map(|schema| (schema.database().clone(), schema.id().clone()).into());
491
492 let item_ids = databases
493 .iter()
494 .map(|database| scx.get_database(database.database_id()))
495 .flat_map(|database| database.schemas().into_iter())
496 .flat_map(|schema| schema.item_ids())
497 .map(|item_id| item_id.into());
498
499 item_ids
500 .chain(schema_ids)
501 .filter(|object_id| object_type_filter(object_id, &object_type, scx))
502 .collect()
503 }
504 GrantTargetSpecificationInner::All(GrantTargetAllSpecification::AllSchemas {
505 schemas,
506 }) => schemas
507 .into_iter()
508 .map(|schema| scx.get_schema(schema.database_spec(), schema.schema_spec()))
509 .flat_map(|schema| schema.item_ids())
510 .map(|item_id| item_id.into())
511 .filter(|object_id| object_type_filter(object_id, &object_type, scx))
512 .collect(),
513 GrantTargetSpecificationInner::Objects { names } => {
514 let mut ids = Vec::with_capacity(names.len());
515 for name in names {
516 ids.push(
517 name.try_into()
519 .map_err(|e| internal_err!("invalid object name: {}", e))?,
520 );
521 }
522 ids
523 }
524 };
525 let target_ids = object_ids.into_iter().map(|id| id.into()).collect();
526 (SystemObjectType::Object(object_type), target_ids)
527 }
528 GrantTargetSpecification::System => {
529 (SystemObjectType::System, vec![SystemObjectId::System])
530 }
531 };
532
533 let mut update_privileges = Vec::with_capacity(target_ids.len());
534
535 for target_id in target_ids {
536 if let SystemObjectId::Object(ObjectId::Schema((_, SchemaSpecifier::Temporary))) =
540 &target_id
541 {
542 sql_bail!(
543 "cannot grant or revoke privileges on schema {} because it is a temporary schema",
544 mz_repr::namespaces::MZ_TEMP_SCHEMA
545 );
546 }
547
548 let actual_object_type = scx.get_system_object_type(&target_id);
550 let mut reference_object_type = actual_object_type.clone();
553
554 if let SystemObjectId::Object(ObjectId::Item(id)) = &target_id {
555 let item = scx.get_item(id);
556 let item_type: ObjectType = item.item_type().into();
557 if (item_type == ObjectType::View
558 || item_type == ObjectType::MaterializedView
559 || item_type == ObjectType::Source)
560 && object_type == SystemObjectType::Object(ObjectType::Table)
561 {
562 reference_object_type = SystemObjectType::Object(ObjectType::Table);
564 } else if SystemObjectType::Object(item_type) != object_type {
565 let object_name = scx.catalog.resolve_full_name(item.name()).to_string();
566 return Err(PlanError::InvalidObjectType {
567 expected_type: object_type,
568 actual_type: actual_object_type,
569 object_name,
570 });
571 }
572 }
573
574 let acl_mode = privilege_spec_to_acl_mode(scx, &privileges, reference_object_type);
579 let acl_from_all = matches!(privileges, PrivilegeSpecification::All);
580
581 let all_object_privileges = scx.catalog.all_object_privileges(reference_object_type);
582 let invalid_privileges = acl_mode.difference(all_object_privileges);
583 if !invalid_privileges.is_empty() {
584 let object_description =
585 ErrorMessageObjectDescription::from_sys_id(&target_id, scx.catalog);
586 return Err(PlanError::InvalidPrivilegeTypes {
587 invalid_privileges,
588 object_description,
589 });
590 }
591
592 let grantor = match &target_id {
599 SystemObjectId::Object(object_id) => scx
600 .catalog
601 .get_owner_id(object_id)
602 .ok_or_else(|| sql_err!("cannot revoke privileges on objects without owners"))?,
603 SystemObjectId::System => scx.catalog.mz_system_role_id(),
604 };
605
606 update_privileges.push(UpdatePrivilege {
607 acl_mode,
608 target_id,
609 grantor,
610 acl_from_all,
611 });
612 }
613
614 let grantees = roles.into_iter().map(|role| role.id).collect();
615
616 Ok(UpdatePrivilegesPlan {
617 update_privileges,
618 grantees,
619 })
620}
621
622fn privilege_spec_to_acl_mode(
623 scx: &StatementContext,
624 privilege_spec: &PrivilegeSpecification,
625 object_type: SystemObjectType,
626) -> AclMode {
627 match privilege_spec {
628 PrivilegeSpecification::All => scx.catalog.all_object_privileges(object_type),
629 PrivilegeSpecification::Privileges(privileges) => privileges
630 .into_iter()
631 .map(|privilege| privilege_to_acl_mode(privilege.clone()))
632 .fold(AclMode::empty(), |accum, acl_mode| accum.union(acl_mode)),
634 }
635}
636
637fn privilege_to_acl_mode(privilege: Privilege) -> AclMode {
638 match privilege {
639 Privilege::SELECT => AclMode::SELECT,
640 Privilege::INSERT => AclMode::INSERT,
641 Privilege::UPDATE => AclMode::UPDATE,
642 Privilege::DELETE => AclMode::DELETE,
643 Privilege::USAGE => AclMode::USAGE,
644 Privilege::CREATE => AclMode::CREATE,
645 Privilege::CREATEROLE => AclMode::CREATE_ROLE,
646 Privilege::CREATEDB => AclMode::CREATE_DB,
647 Privilege::CREATECLUSTER => AclMode::CREATE_CLUSTER,
648 Privilege::CREATENETWORKPOLICY => AclMode::CREATE_NETWORK_POLICY,
649 }
650}
651
652pub fn describe_alter_default_privileges(
653 _: &StatementContext,
654 _: AlterDefaultPrivilegesStatement<Aug>,
655) -> Result<StatementDesc, PlanError> {
656 Ok(StatementDesc::new(None))
657}
658
659pub fn plan_alter_default_privileges(
660 scx: &StatementContext,
661 AlterDefaultPrivilegesStatement {
662 target_roles,
663 target_objects,
664 grant_or_revoke,
665 }: AlterDefaultPrivilegesStatement<Aug>,
666) -> Result<Plan, PlanError> {
667 let object_type: ObjectType = (*grant_or_revoke.object_type()).into();
668 match object_type {
669 ObjectType::View | ObjectType::MaterializedView | ObjectType::Source => sql_bail!(
670 "{object_type}S is not valid for ALTER DEFAULT PRIVILEGES, use TABLES instead"
671 ),
672 ObjectType::Sink
673 | ObjectType::MetricSink
674 | ObjectType::ClusterReplica
675 | ObjectType::Role
676 | ObjectType::Func => {
677 sql_bail!("{object_type}S do not have privileges")
678 }
679 ObjectType::Cluster | ObjectType::Database
680 if matches!(
681 target_objects,
682 GrantTargetAllSpecification::AllDatabases { .. }
683 ) =>
684 {
685 sql_bail!("cannot specify {object_type}S and IN DATABASE")
686 }
687
688 ObjectType::Cluster | ObjectType::Database | ObjectType::Schema
689 if matches!(
690 target_objects,
691 GrantTargetAllSpecification::AllSchemas { .. }
692 ) =>
693 {
694 sql_bail!("cannot specify {object_type}S and IN SCHEMA")
695 }
696 ObjectType::Table
697 | ObjectType::Index
698 | ObjectType::Type
699 | ObjectType::Secret
700 | ObjectType::Connection
701 | ObjectType::Cluster
702 | ObjectType::Database
703 | ObjectType::Schema
704 | ObjectType::NetworkPolicy => {}
705 }
706
707 let acl_mode = privilege_spec_to_acl_mode(
708 scx,
709 grant_or_revoke.privileges(),
710 SystemObjectType::Object(object_type),
711 );
712 let all_object_privileges = scx
713 .catalog
714 .all_object_privileges(SystemObjectType::Object(object_type));
715 let invalid_privileges = acl_mode.difference(all_object_privileges);
716 if !invalid_privileges.is_empty() {
717 let object_description =
718 ErrorMessageObjectDescription::from_object_type(SystemObjectType::Object(object_type));
719 return Err(PlanError::InvalidPrivilegeTypes {
720 invalid_privileges,
721 object_description,
722 });
723 }
724
725 let target_roles = match target_roles {
726 TargetRoleSpecification::Roles(roles) => roles.into_iter().map(|role| role.id).collect(),
727 TargetRoleSpecification::AllRoles => vec![RoleId::Public],
728 };
729 let mut privilege_objects = Vec::with_capacity(target_roles.len() * target_objects.len());
730 for target_role in target_roles {
731 match &target_objects {
732 GrantTargetAllSpecification::All => privilege_objects.push(DefaultPrivilegeObject {
733 role_id: target_role,
734 database_id: None,
735 schema_id: None,
736 object_type,
737 }),
738 GrantTargetAllSpecification::AllDatabases { databases } => {
739 for database in databases {
740 privilege_objects.push(DefaultPrivilegeObject {
741 role_id: target_role,
742 database_id: Some(*database.database_id()),
743 schema_id: None,
744 object_type,
745 });
746 }
747 }
748 GrantTargetAllSpecification::AllSchemas { schemas } => {
749 for schema in schemas {
750 privilege_objects.push(DefaultPrivilegeObject {
751 role_id: target_role,
752 database_id: schema.database_spec().id(),
753 schema_id: Some(schema.schema_spec().into()),
754 object_type,
755 });
756 }
757 }
758 }
759 }
760
761 let privilege_acl_items = grant_or_revoke
762 .roles()
763 .into_iter()
764 .map(|grantee| DefaultPrivilegeAclItem {
765 grantee: grantee.id,
766 acl_mode,
767 })
768 .collect();
769
770 let is_grant = match grant_or_revoke {
771 AbbreviatedGrantOrRevokeStatement::Grant(_) => true,
772 AbbreviatedGrantOrRevokeStatement::Revoke(_) => false,
773 };
774
775 Ok(Plan::AlterDefaultPrivileges(AlterDefaultPrivilegesPlan {
776 privilege_objects,
777 privilege_acl_items,
778 is_grant,
779 }))
780}
781
782pub fn describe_reassign_owned(
783 _: &StatementContext,
784 _: ReassignOwnedStatement<Aug>,
785) -> Result<StatementDesc, PlanError> {
786 Ok(StatementDesc::new(None))
787}
788
789pub fn plan_reassign_owned(
790 scx: &StatementContext,
791 ReassignOwnedStatement {
792 old_roles,
793 new_role,
794 }: ReassignOwnedStatement<Aug>,
795) -> Result<Plan, PlanError> {
796 let old_roles: BTreeSet<_> = old_roles.into_iter().map(|role| role.id).collect();
797 let mut reassign_ids: Vec<ObjectId> = Vec::new();
798
799 for replica in scx.catalog.get_cluster_replicas() {
801 if old_roles.contains(&replica.owner_id()) {
802 reassign_ids.push((replica.cluster_id(), replica.replica_id()).into());
803 }
804 }
805 for cluster in scx.catalog.get_clusters() {
807 if old_roles.contains(&cluster.owner_id()) {
808 reassign_ids.push(cluster.id().into());
809 }
810 }
811 for item in scx.catalog.get_items() {
813 if old_roles.contains(&item.owner_id()) {
814 reassign_ids.push(item.id().into());
815 }
816 }
817 for schema in scx.catalog.get_schemas() {
819 if !schema.id().is_temporary() {
820 if old_roles.contains(&schema.owner_id()) {
821 reassign_ids.push((*schema.database(), *schema.id()).into())
822 }
823 }
824 }
825 for database in scx.catalog.get_databases() {
827 if old_roles.contains(&database.owner_id()) {
828 reassign_ids.push(database.id().into());
829 }
830 }
831 for network_policy in scx.catalog.get_network_policies() {
833 if old_roles.contains(&network_policy.owner_id()) {
834 reassign_ids.push(ObjectId::NetworkPolicy(network_policy.id()));
835 }
836 }
837
838 let system_ids: Vec<_> = reassign_ids.iter().filter(|id| id.is_system()).collect();
839 if !system_ids.is_empty() {
840 let mut owners = system_ids
841 .into_iter()
842 .filter_map(|object_id| scx.catalog.get_owner_id(object_id))
843 .collect::<BTreeSet<_>>()
844 .into_iter()
845 .map(|role_id| scx.catalog.get_role(&role_id).name().quoted());
846 sql_bail!(
847 "cannot reassign objects owned by role {} because they are required by the database system",
848 owners.join(", "),
849 );
850 }
851
852 Ok(Plan::ReassignOwned(ReassignOwnedPlan {
853 old_roles: old_roles.into_iter().collect(),
854 new_role: new_role.id,
855 reassign_ids,
856 }))
857}