postrust_core/schema_cache/
mod.rs1mod table;
7mod relationship;
8mod routine;
9mod queries;
10
11pub use table::{Table, Column, ColumnMap, TablesMap};
12pub use relationship::{Relationship, Cardinality, Junction, RelationshipsMap};
13pub use routine::{Routine, RoutineParam, RetType, FuncVolatility, RoutineMap};
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(&self, qi: &QualifiedIdentifier, schema: &str) -> Option<&Vec<Relationship>> {
84 self.relationships.get(&(qi.clone(), schema.to_string()))
85 }
86
87 pub fn get_routines(&self, qi: &QualifiedIdentifier) -> Option<&Vec<Routine>> {
89 self.routines.get(qi)
90 }
91
92 pub fn is_valid_timezone(&self, tz: &str) -> bool {
94 self.timezones.contains(tz)
95 }
96
97 pub fn summary(&self) -> String {
99 format!(
100 "SchemaCache: {} tables, {} relationship sets, {} routines, PG {}",
101 self.tables.len(),
102 self.relationships.len(),
103 self.routines.len(),
104 self.pg_version
105 )
106 }
107
108 pub fn find_relationship(
110 &self,
111 from: &QualifiedIdentifier,
112 to_name: &str,
113 schema: &str,
114 ) -> Option<&Relationship> {
115 self.get_relationships(from, schema)?
116 .iter()
117 .find(|r| match r {
118 Relationship::ForeignKey { foreign_table, .. } => {
119 foreign_table.name == to_name
120 }
121 Relationship::Computed { foreign_table, .. } => {
122 foreign_table.name == to_name
123 }
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}