Skip to main content

uqa_storage/catalog/
schema.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use serde::{Deserialize, Serialize};
8
9/// Grantable privileges carried by one schema ACL path.
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
11pub struct SchemaPrivileges {
12    #[serde(default)]
13    pub usage: bool,
14    #[serde(default)]
15    pub create: bool,
16}
17
18impl SchemaPrivileges {
19    pub const ALL: Self = Self {
20        usage: true,
21        create: true,
22    };
23
24    #[must_use]
25    pub const fn is_empty(self) -> bool {
26        !self.usage && !self.create
27    }
28
29    #[must_use]
30    pub const fn intersects(self, other: Self) -> bool {
31        self.usage && other.usage || self.create && other.create
32    }
33
34    pub fn insert(&mut self, other: Self) {
35        self.usage |= other.usage;
36        self.create |= other.create;
37    }
38
39    pub fn remove(&mut self, other: Self) {
40        self.usage &= !other.usage;
41        self.create &= !other.create;
42    }
43}
44
45/// One explicit schema ACL path. `None` on [`SchemaRow::acl`] retains the owner-only default privileges of an ordinary newly created schema.
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct SchemaAclEntry {
48    pub role: String,
49    /// Legacy persisted entries without an explicit grantor originate from the schema owner.
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub grantor: Option<String>,
52    #[serde(default)]
53    pub privileges: SchemaPrivileges,
54    #[serde(default)]
55    pub grant_options: SchemaPrivileges,
56}
57
58/// Durable schema ownership and ACL metadata.
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub struct SchemaRow {
61    pub name: String,
62    /// SQL role that owns the schema. Catalogs written before schema security belonged to the bootstrap role.
63    #[serde(default = "default_schema_role_owner")]
64    pub role_owner: String,
65    /// Explicit ACL paths. `None` represents the owner-only default for an ordinary schema.
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub acl: Option<Vec<SchemaAclEntry>>,
68}
69
70impl SchemaRow {
71    #[must_use]
72    pub fn legacy(name: impl Into<String>) -> Self {
73        let name = name.into();
74        let acl = (name == "public").then(|| {
75            vec![
76                SchemaAclEntry {
77                    role: "uqa".into(),
78                    grantor: Some("uqa".into()),
79                    privileges: SchemaPrivileges::ALL,
80                    grant_options: SchemaPrivileges::default(),
81                },
82                SchemaAclEntry {
83                    role: "PUBLIC".into(),
84                    grantor: Some("uqa".into()),
85                    privileges: SchemaPrivileges {
86                        usage: true,
87                        create: false,
88                    },
89                    grant_options: SchemaPrivileges::default(),
90                },
91            ]
92        });
93        Self {
94            name,
95            role_owner: default_schema_role_owner(),
96            acl,
97        }
98    }
99}
100
101fn default_schema_role_owner() -> String {
102    "uqa".into()
103}