uqa_sql/catalog/
foreign_server.rs1use super::roles::{identity::RoleSubject, RoleDefinition, RoleIdentity};
10use crate::SQLError;
11use serde::{Deserialize, Serialize};
12use std::collections::{BTreeMap, BTreeSet};
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(deny_unknown_fields)]
16pub struct ForeignServerMetadata {
17 pub oid: u32,
18 pub object_id: [u8; 16],
19 pub owner: RoleIdentity,
20 pub server_type: Option<String>,
21 pub version: Option<String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub option_order: Option<Vec<String>>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub wrapper_reference: Option<super::foreign_wrapper::ForeignWrapperReference>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ForeignServerDefinition {
32 pub name: String,
33 pub fdw_type: String,
34 pub options: BTreeMap<String, String>,
35 pub metadata: ForeignServerMetadata,
36}
37
38impl ForeignServerDefinition {
39 pub fn bound_wrapper<'a>(
40 &self,
41 wrappers: &'a super::foreign_wrapper::ForeignWrappers,
42 ) -> Result<&'a super::foreign_wrapper::ForeignWrapperDefinition, SQLError> {
43 let reference = self.metadata.wrapper_reference.ok_or_else(|| {
44 SQLError::Internal(format!(
45 "foreign server `{}` has no wrapper identity",
46 self.name
47 ))
48 })?;
49 super::foreign_wrapper::bound_wrapper(wrappers, &self.fdw_type, reference)
50 }
51}
52
53pub fn validate_foreign_servers(
55 servers: &BTreeMap<String, ForeignServerDefinition>,
56 roles: &BTreeMap<String, RoleDefinition>,
57) -> Result<(), SQLError> {
58 let mut oids = BTreeSet::new();
59 let mut identities = BTreeSet::new();
60 for (name, server) in servers {
61 let metadata = &server.metadata;
62 validate_foreign_server_name(name)?;
63 if name != &server.name {
64 return Err(invalid(name, "invalid catalog name"));
65 }
66 if metadata.oid < super::oids::FIRST_NORMAL_OBJECT_ID || !oids.insert(metadata.oid) {
67 return Err(invalid(name, "invalid or duplicate catalog OID"));
68 }
69 if metadata.object_id == [0; 16] || !identities.insert(metadata.object_id) {
70 return Err(invalid(name, "invalid or duplicate object identity"));
71 }
72 if let Some(order) = &metadata.option_order {
73 let names = order.iter().collect::<BTreeSet<_>>();
74 if names.len() != order.len()
75 || order.len() != server.options.len()
76 || order.iter().any(|name| !server.options.contains_key(name))
77 {
78 return Err(invalid(name, "option order disagrees with stored options"));
79 }
80 }
81 if !metadata.owner.is_valid() || metadata.owner.role_definition(roles).is_none() {
82 return Err(invalid(name, "owner references a missing or replaced role"));
83 }
84 }
85 Ok(())
86}
87
88pub fn validate_foreign_server_name(name: &str) -> Result<(), SQLError> {
90 if name.is_empty() {
91 return Err(invalid(name, "invalid catalog name"));
92 }
93 Ok(())
94}
95
96fn invalid(name: &str, reason: &str) -> SQLError {
97 SQLError::Internal(format!("foreign server `{name}`: {reason}"))
98}
99
100#[cfg(test)]
101mod tests;