uqa-execution 0.4.5

Volcano physical operators with row-batch pipelines
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

//! Validate owned ACL candidates while retaining their authorization guards.

use super::{
    context::TableGrantContext,
    targets::{
        validated_foreign_table_grant_targets, validated_table_grant_targets,
        validated_view_grant_targets,
    },
    updates::{system_privilege_updates, table_privilege_updates, TablePrivilegeUpdate},
};
use crate::catalog::security::{
    roles::dependencies::RoleDependencyCandidate, system_relations::SystemPrivilegeUpdate,
};
use uqa_sql::catalog::security::acl_command::{AclCommandRoles, ResolvedAclRoles};
use uqa_sql::{
    ast::{GrantTableStmt, GrantTableTarget},
    catalog::security::{
        table::{requested_acl_privileges, RequestedTablePrivileges},
        table_grants::{
            foreign_table_privilege_updates, validate_table_acl_roles,
            validate_table_grant_target_kinds, view_privilege_updates, ForeignTablePrivilegeUpdate,
            ResolvedTableGrantTarget, TableGrantApplication, ViewPrivilegeUpdate,
        },
    },
    SQLError,
};

pub(super) struct PreparedTableGrant<'a> {
    pub updates: Vec<TablePrivilegeUpdate<'a>>,
    pub view_updates: Vec<ViewPrivilegeUpdate>,
    pub foreign_updates: Vec<ForeignTablePrivilegeUpdate>,
    pub system_updates: Vec<SystemPrivilegeUpdate>,
    pub acl_updates: Vec<super::tuples::RelationPrivilegeUpdate>,
    pub notices: Vec<(&'static str, String)>,
}

pub(super) fn resolve_roles(
    context: &TableGrantContext<'_>,
    statement: &GrantTableStmt,
    command_roles: &mut AclCommandRoles,
    roles: &std::collections::BTreeMap<String, uqa_sql::catalog::roles::RoleDefinition>,
) -> Result<ResolvedAclRoles, SQLError> {
    command_roles.resolve_validated(
        context.names,
        roles,
        &statement.grantees,
        statement.grantor.as_ref(),
        |resolved| {
            validate_table_acl_roles(
                statement,
                &resolved.grantees,
                resolved.requested_grantor.as_deref(),
                &resolved.current_user,
                roles,
            )
        },
    )
}

pub(super) fn prepare<'a>(
    context: &'a TableGrantContext<'_>,
    statement: &GrantTableStmt,
    targets: &[ResolvedTableGrantTarget],
    command_roles: &mut AclCommandRoles,
) -> Result<RoleDependencyCandidate<'a, PreparedTableGrant<'a>>, SQLError> {
    let roles = context.roles.role_definitions();
    let ResolvedAclRoles {
        grantees,
        current_user,
        ..
    } = resolve_roles(context, statement, command_roles, &roles)?;

    validate_table_grant_target_kinds(statement, targets)?;
    let has_table_relations = targets.iter().any(|target| {
        matches!(
            target.kind,
            "table" | "view" | "materialized view" | "foreign table"
        )
    });
    let requested_privileges = if has_table_relations
        || matches!(
            statement.target,
            GrantTableTarget::AllTablesInSchemas { .. }
        ) {
        requested_acl_privileges(&statement.privileges)?
    } else {
        RequestedTablePrivileges {
            table: Vec::new(),
            columns: Vec::new(),
        }
    };
    let memberships = context.roles.role_memberships();
    let table_targets = validated_table_grant_targets(context, targets, &requested_privileges)?;
    let view_targets = validated_view_grant_targets(context, targets, &requested_privileges)?;
    let foreign_targets =
        validated_foreign_table_grant_targets(context, targets, &requested_privileges)?;
    let mut notices = Vec::new();
    let mut dependencies = std::collections::BTreeSet::new();
    let application = TableGrantApplication {
        statement,
        grantees: &grantees,
        requested: &requested_privileges,
        current_user: &current_user,
        roles: &roles,
        memberships: &memberships,
    };
    let mut updates =
        table_privilege_updates(table_targets, &application, &mut notices, &mut dependencies)?;
    let mut view_updates =
        view_privilege_updates(view_targets, &application, &mut notices, &mut dependencies)?;
    let mut foreign_updates = foreign_table_privilege_updates(
        foreign_targets,
        &application,
        &mut notices,
        &mut dependencies,
    )?;
    let system_updates = system_privilege_updates(
        context,
        targets,
        &application,
        &mut notices,
        &mut dependencies,
    )?;
    let acl_updates = super::tuples::prepare(
        context,
        targets,
        &application,
        &mut updates,
        &mut view_updates,
        &mut foreign_updates,
    )?;
    Ok(RoleDependencyCandidate {
        value: PreparedTableGrant {
            updates,
            view_updates,
            foreign_updates,
            system_updates,
            acl_updates,
            notices,
        },
        memberships,
        roles,
        dependencies,
    })
}