1use std::collections::BTreeSet;
4
5use rupg_catalog::RelKind;
6
7use crate::Analyzer;
8use crate::expr::{Expr, ExprKind};
9use crate::insert::Given;
10use crate::select::Query;
11
12pub(crate) const ACL_INSERT: u32 = 1 << 0;
14pub(crate) const ACL_SELECT: u32 = 1 << 1;
16pub(crate) const ACL_UPDATE: u32 = 1 << 2;
18pub(crate) const ACL_DELETE: u32 = 1 << 3;
20
21#[derive(Clone, Debug, Default, PartialEq, Eq)]
23pub struct PermInfo {
24 pub relid: u32,
26 pub check_as: u32,
28 pub invoker: bool,
30 pub required: u32,
32 pub selected: BTreeSet<i16>,
34 pub inserted: BTreeSet<i16>,
36 pub updated: BTreeSet<i16>,
38}
39
40fn shift(query: &mut Query, offset: usize) {
42 for relation in &mut query.relations {
43 if let Some(perm) = &mut relation.perm {
44 *perm += offset;
45 }
46 if let Some(sub) = relation.subquery.as_deref_mut() {
47 shift(sub, offset);
48 }
49 }
50}
51
52pub(crate) fn forget(query: &mut Query) {
54 for relation in &mut query.relations {
55 relation.perm = None;
56 if let Some(sub) = relation.subquery.as_deref_mut() {
57 forget(sub);
58 }
59 }
60}
61
62pub fn option_on(options: &[String], name: &str) -> bool {
64 options.iter().any(|option| {
65 option.split_once('=').is_some_and(|(option, value)| {
66 option == name
67 && matches!(
68 value.to_ascii_lowercase().as_str(),
69 "true" | "on" | "yes" | "1" | "t" | "y"
70 )
71 })
72 })
73}
74
75impl Analyzer<'_> {
76 pub(crate) fn add_perm(&mut self, relid: u32, required: u32) -> usize {
78 self.perms.push(PermInfo { relid, required, ..PermInfo::default() });
79 self.perms.len() - 1
80 }
81
82 pub(crate) fn add_view_perms(&mut self, view: u32, query: &mut Query, perms: Vec<PermInfo>) {
84 let rel = self.env.catalog().and_then(|c| c.relation(view));
85 let invoker = rel.is_some_and(|rel| {
86 rel.kind == RelKind::View && option_on(&rel.options, "security_invoker")
87 });
88 let owner = match rel {
89 Some(rel) if !invoker => rel.owner,
90 _ => 0,
91 };
92 shift(query, self.perms.len());
93 self.funcs.append(&mut query.funcs);
94 for mut perm in perms {
95 if perm.check_as == 0 && !perm.invoker {
96 perm.check_as = owner;
97 perm.invoker = invoker;
98 }
99 self.perms.push(perm);
100 }
101 }
102
103 pub(crate) fn note_funcs(&mut self, expr: &Expr) {
105 expr.find(0, &mut |e, _| {
106 if let ExprKind::Func(f) = &e.kind {
107 self.funcs.push(f.oid);
108 }
109 None::<()>
110 });
111 }
112
113 pub(crate) fn mark_select(&mut self, expr: &Expr) {
115 let mut vars = Vec::new();
116 expr.find(0, &mut |e, depth| {
117 if let ExprKind::Var(var) = &e.kind
118 && var.levels_up >= depth
119 {
120 vars.push((var.levels_up - depth, var.relation, var.attnum));
121 }
122 None::<()>
123 });
124 for (level, relation, attnum) in vars {
125 if let Some(perm) = self.relation_perm(level, relation) {
126 self.perms[perm].required |= ACL_SELECT;
127 self.perms[perm].selected.insert(attnum);
128 }
129 }
130 }
131
132 pub(crate) fn mark_relation(&mut self, level: usize, relation: usize) {
134 if let Some(perm) = self.relation_perm(level, relation) {
135 self.perms[perm].required |= ACL_SELECT;
136 }
137 }
138
139 fn relation_perm(&self, level: usize, relation: usize) -> Option<usize> {
141 let (_, scope) = self.levels().nth(level)?;
142 scope.relations.get(relation)?.perm
143 }
144
145 pub(crate) fn target_perm_mut(&mut self) -> Option<&mut PermInfo> {
147 self.target_perm.and_then(|perm| self.perms.get_mut(perm))
148 }
149
150 pub(crate) fn mark_inserted(&mut self, given: &[Given]) {
152 if let Some(perm) = self.target_perm_mut() {
153 perm.inserted
154 .extend(given.iter().map(|(i, _, _)| i16::try_from(i + 1).unwrap_or(i16::MAX)));
155 }
156 }
157
158 pub(crate) fn mark_updated(&mut self, columns: &[i16]) {
160 if let Some(perm) = self.target_perm_mut() {
161 perm.required |= ACL_UPDATE;
162 perm.updated.extend(columns.iter().copied());
163 }
164 }
165
166 pub(crate) fn mark_locked(&mut self, query: &Query) {
168 for mark in &query.row_marks {
169 if let Some(perm) = query.relations.get(mark.relation).and_then(|r| r.perm) {
170 self.perms[perm].required |= ACL_UPDATE;
171 }
172 }
173 for relation in &query.relations {
174 if let Some(sub) = relation.subquery.as_deref() {
175 self.mark_locked(sub);
176 }
177 }
178 }
179}