postrust_core/schema_cache/
mod.rs1mod queries;
7mod relationship;
8mod routine;
9mod table;
10
11pub use relationship::{Cardinality, Junction, Relationship, RelationshipsMap};
12pub use routine::{FuncVolatility, RetType, Routine, RoutineMap, RoutineParam};
13pub use table::{Column, ColumnMap, Table, TablesMap};
14
15use crate::api_request::QualifiedIdentifier;
16use crate::error::{Error, Result};
17use sqlx::PgPool;
18use std::collections::HashSet;
19use std::sync::Arc;
20use tracing::info;
21
22#[derive(Clone, Debug)]
24pub struct SchemaCache {
25 pub tables: TablesMap,
27 pub relationships: RelationshipsMap,
29 pub routines: RoutineMap,
31 pub timezones: HashSet<String>,
33 pub pg_version: i32,
35}
36
37impl SchemaCache {
38 pub async fn load(pool: &PgPool, schemas: &[String]) -> Result<Self> {
40 info!("Loading schema cache for schemas: {:?}", schemas);
41
42 let pg_version = queries::get_pg_version(pool).await?;
44 info!("PostgreSQL version: {}", pg_version);
45
46 let tables = queries::load_tables(pool, schemas).await?;
48 info!("Loaded {} tables/views", tables.len());
49
50 let relationships = queries::load_relationships(pool, schemas).await?;
52 info!("Loaded {} relationship sets", relationships.len());
53
54 let routines = queries::load_routines(pool, schemas).await?;
56 info!("Loaded {} routines", routines.len());
57
58 let timezones = queries::load_timezones(pool).await?;
60 info!("Loaded {} timezones", timezones.len());
61
62 Ok(Self {
63 tables,
64 relationships,
65 routines,
66 timezones,
67 pg_version,
68 })
69 }
70
71 pub fn get_table(&self, qi: &QualifiedIdentifier) -> Option<&Table> {
73 self.tables.get(qi)
74 }
75
76 pub fn require_table(&self, qi: &QualifiedIdentifier) -> Result<&Table> {
78 self.get_table(qi)
79 .ok_or_else(|| Error::TableNotFound(qi.to_string()))
80 }
81
82 pub fn get_relationships(
84 &self,
85 qi: &QualifiedIdentifier,
86 schema: &str,
87 ) -> Option<&Vec<Relationship>> {
88 self.relationships.get(&(qi.clone(), schema.to_string()))
89 }
90
91 pub fn get_routines(&self, qi: &QualifiedIdentifier) -> Option<&Vec<Routine>> {
93 self.routines.get(qi)
94 }
95
96 pub fn is_valid_timezone(&self, tz: &str) -> bool {
98 self.timezones.contains(tz)
99 }
100
101 pub fn summary(&self) -> String {
103 format!(
104 "SchemaCache: {} tables, {} relationship sets, {} routines, PG {}",
105 self.tables.len(),
106 self.relationships.len(),
107 self.routines.len(),
108 self.pg_version
109 )
110 }
111
112 pub fn find_relationship(
114 &self,
115 from: &QualifiedIdentifier,
116 to_name: &str,
117 schema: &str,
118 ) -> Option<&Relationship> {
119 self.get_relationships(from, schema)?
120 .iter()
121 .find(|r| match r {
122 Relationship::ForeignKey { foreign_table, .. } => foreign_table.name == to_name,
123 Relationship::Computed { foreign_table, .. } => foreign_table.name == to_name,
124 })
125 }
126}
127
128#[derive(Clone)]
130pub struct SchemaCacheRef(Arc<tokio::sync::RwLock<Option<SchemaCache>>>);
131
132impl SchemaCacheRef {
133 pub fn new() -> Self {
135 Self(Arc::new(tokio::sync::RwLock::new(None)))
136 }
137
138 pub fn from_static(cache: SchemaCache) -> Self {
140 Self(Arc::new(tokio::sync::RwLock::new(Some(cache))))
141 }
142
143 pub async fn load(&self, pool: &PgPool, schemas: &[String]) -> Result<()> {
145 let cache = SchemaCache::load(pool, schemas).await?;
146 let mut guard = self.0.write().await;
147 *guard = Some(cache);
148 Ok(())
149 }
150
151 pub async fn get(&self) -> Result<tokio::sync::RwLockReadGuard<'_, Option<SchemaCache>>> {
153 let guard = self.0.read().await;
154 if guard.is_none() {
155 return Err(Error::SchemaCacheNotLoaded);
156 }
157 Ok(guard)
158 }
159
160 pub async fn is_loaded(&self) -> bool {
162 self.0.read().await.is_some()
163 }
164}
165
166impl Default for SchemaCacheRef {
167 fn default() -> Self {
168 Self::new()
169 }
170}