Skip to main content

pgmt/render/
grant.rs

1//! SQL rendering for grant operations
2
3use crate::catalog::id::DbObjectId;
4use crate::diff::operations::GrantOperation;
5use crate::render::{RenderedSql, SqlRenderer};
6
7impl SqlRenderer for GrantOperation {
8    fn to_sql(&self) -> Vec<RenderedSql> {
9        match self {
10            GrantOperation::Grant { grant } => {
11                vec![RenderedSql::new(
12                    crate::render::sql::render_grant_statement(grant),
13                )]
14            }
15            GrantOperation::Revoke { grant } => {
16                vec![RenderedSql::new(
17                    crate::render::sql::render_revoke_statement(grant),
18                )]
19            }
20            GrantOperation::GrantColumns(cg) => {
21                vec![RenderedSql::new(
22                    crate::render::sql::render_column_grant_statement(cg),
23                )]
24            }
25            GrantOperation::RevokeColumns(cg) => {
26                vec![RenderedSql::new(
27                    crate::render::sql::render_column_revoke_statement(cg),
28                )]
29            }
30        }
31    }
32
33    fn db_object_id(&self) -> DbObjectId {
34        match self {
35            GrantOperation::Grant { grant } => DbObjectId::Grant { id: grant.id() },
36            GrantOperation::Revoke { grant } => DbObjectId::Grant { id: grant.id() },
37            GrantOperation::GrantColumns(cg) => DbObjectId::Grant {
38                id: cg.rep_id.clone(),
39            },
40            GrantOperation::RevokeColumns(cg) => DbObjectId::Grant {
41                id: cg.rep_id.clone(),
42            },
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use crate::catalog::grant::{Grant, GranteeType};
51    use crate::catalog::id::DbObjectId;
52    use crate::catalog::target::AttrTarget;
53    use crate::render::Safety;
54
55    fn create_table_grant() -> Grant {
56        Grant {
57            grantee: GranteeType::Role("app_user".to_string()),
58            target: AttrTarget::object(DbObjectId::Table {
59                schema: "public".to_string(),
60                name: "users".to_string(),
61            }),
62            privileges: vec!["SELECT".to_string(), "INSERT".to_string()],
63            with_grant_option: false,
64            depends_on: vec![],
65            object_owner: "admin".to_string(),
66            is_default_acl: false,
67        }
68    }
69
70    #[test]
71    fn test_render_grant() {
72        let grant = create_table_grant();
73        let op = GrantOperation::Grant { grant };
74        let rendered = op.to_sql();
75        assert_eq!(rendered.len(), 1);
76        assert!(rendered[0].sql.contains("GRANT"));
77        assert!(rendered[0].sql.contains("SELECT"));
78        assert!(rendered[0].sql.contains("INSERT"));
79        assert!(rendered[0].sql.contains("app_user"));
80    }
81
82    #[test]
83    fn test_render_revoke() {
84        let grant = create_table_grant();
85        let op = GrantOperation::Revoke { grant };
86        let rendered = op.to_sql();
87        assert_eq!(rendered.len(), 1);
88        assert!(rendered[0].sql.contains("REVOKE"));
89    }
90
91    #[test]
92    fn test_render_grant_to_public() {
93        let grant = Grant {
94            grantee: GranteeType::Public,
95            target: AttrTarget::object(DbObjectId::Function {
96                schema: "public".to_string(),
97                name: "my_func".to_string(),
98                arguments: "integer".to_string(),
99            }),
100            privileges: vec!["EXECUTE".to_string()],
101            with_grant_option: false,
102            depends_on: vec![],
103            object_owner: "admin".to_string(),
104            is_default_acl: false,
105        };
106        let op = GrantOperation::Grant { grant };
107        let rendered = op.to_sql();
108        assert!(rendered[0].sql.contains("PUBLIC"));
109    }
110
111    #[test]
112    fn test_has_destructive_sql() {
113        let grant = create_table_grant();
114        let grant_op = GrantOperation::Grant {
115            grant: grant.clone(),
116        };
117        let revoke_op = GrantOperation::Revoke { grant };
118
119        // Grants/revokes don't destroy data - permissions can be re-granted
120        assert!(
121            !grant_op
122                .to_sql()
123                .iter()
124                .any(|s| s.safety == Safety::Destructive)
125        );
126        assert!(
127            !revoke_op
128                .to_sql()
129                .iter()
130                .any(|s| s.safety == Safety::Destructive)
131        );
132    }
133
134    #[test]
135    fn test_db_object_id() {
136        let grant = create_table_grant();
137        let op = GrantOperation::Grant {
138            grant: grant.clone(),
139        };
140        let expected_id = grant.id();
141        assert_eq!(op.db_object_id(), DbObjectId::Grant { id: expected_id });
142    }
143}