Parse an `ON DENY` clause from DDL parts. Syntax: - `ON DENY SILENT` - `ON DENY ERROR 'CODE' MESSAGE 'text' [DETAIL 'text']`
(parts: &[&str])
| 121 | /// - `ON DENY SILENT` |
| 122 | /// - `ON DENY ERROR 'CODE' MESSAGE 'text' [DETAIL 'text']` |
| 123 | pub fn parse_on_deny(parts: &[&str]) -> crate::Result<DenyMode> { |
| 124 | if parts.is_empty() { |
| 125 | return Ok(DenyMode::Silent); |
| 126 | } |
| 127 | |
| 128 | let mode = parts[0].to_uppercase(); |
| 129 | match mode.as_str() { |
| 130 | "SILENT" => Ok(DenyMode::Silent), |
| 131 | "ERROR" => { |
| 132 | if parts.len() < 2 { |
| 133 | return Err(crate::Error::BadRequest { |
| 134 | detail: "ON DENY ERROR requires a code: ON DENY ERROR 'CODE' MESSAGE '...'" |
| 135 | .to_string(), |
| 136 | }); |
| 137 | } |
| 138 | let code = parts[1].trim_matches('\'').to_string(); |
| 139 | |
| 140 | let message = parts |
| 141 | .iter() |
| 142 | .position(|p| p.to_uppercase() == "MESSAGE") |
| 143 | .and_then(|i| { |
| 144 | // Join everything after MESSAGE until DETAIL or end. |
| 145 | let msg_start = i + 1; |
| 146 | let msg_end = parts[msg_start..] |
| 147 | .iter() |
| 148 | .position(|p| p.to_uppercase() == "DETAIL") |
| 149 | .map(|j| msg_start + j) |
| 150 | .unwrap_or(parts.len()); |
| 151 | if msg_start < msg_end { |
| 152 | Some( |
| 153 | parts[msg_start..msg_end] |
| 154 | .join(" ") |
| 155 | .trim_matches('\'') |
| 156 | .to_string(), |
| 157 | ) |
| 158 | } else { |
| 159 | None |
| 160 | } |
| 161 | }) |
| 162 | .unwrap_or_else(|| format!("Access denied by RLS policy (code: {code})")); |
| 163 | |
| 164 | let detail = parts |
| 165 | .iter() |
| 166 | .position(|p| p.to_uppercase() == "DETAIL") |
| 167 | .and_then(|i| { |
| 168 | if i + 1 < parts.len() { |
| 169 | Some(parts[i + 1..].join(" ").trim_matches('\'').to_string()) |
| 170 | } else { |
| 171 | None |
| 172 | } |
| 173 | }); |
| 174 | |
| 175 | Ok(DenyMode::Error(DenyError { |
| 176 | code, |
| 177 | message, |
| 178 | detail, |
| 179 | })) |
| 180 | } |