valence_backend_postgres/
backend.rs1use sqlx::postgres::PgPool;
4
5use valence_backend_sql::{
6 apply_ttl_policy_postgres, create_record_postgres, define_unique_index_postgres,
7 delete_record_postgres, ensure_edges_postgres, ensure_table_postgres, execute_select_postgres,
8 get_edge_sources_postgres, get_edge_targets_postgres, get_record_postgres,
9 merge_record_postgres, relate_edge_postgres, sql_capabilities, ttl_deferred,
10 unrelate_edge_postgres, update_record_postgres,
11};
12use valence_core::backend::DatabaseBackend;
13use valence_core::compiled_query::CompiledQuery;
14use valence_core::error::{Error, Result};
15use valence_core::record_id::RecordId;
16use valence_core::ttl::SchemaTtlPolicy;
17use valence_core::{Database, DatabaseFromEngine, KnownEngines};
18
19pub const ENGINE_ID: &str = KnownEngines::POSTGRES;
21
22pub const PRIMARY: DatabaseFromEngine = Database::from_engine("primary", ENGINE_ID);
24
25#[derive(Debug, Clone)]
63pub struct PostgresBackend {
64 pool: PgPool,
65}
66
67impl PostgresBackend {
68 pub fn builder() -> crate::config::PostgresBackendBuilder {
70 crate::config::PostgresBackendBuilder::new()
71 }
72
73 pub async fn from_env() -> Result<Self> {
79 Self::builder().from_env_defaults().build().await
80 }
81
82 pub async fn connect(url: &str) -> Result<Self> {
88 let pool = PgPool::connect(url)
89 .await
90 .map_err(|e| Error::database(e.to_string()))?;
91 ensure_edges_postgres(&pool).await?;
92 Ok(Self { pool })
93 }
94
95 pub fn pool(&self) -> &PgPool {
97 &self.pool
98 }
99}
100
101#[async_trait::async_trait]
102impl DatabaseBackend for PostgresBackend {
103 fn engine_id(&self) -> &'static str {
104 ENGINE_ID
105 }
106
107 fn capabilities(&self) -> valence_core::BackendCapabilities {
108 sql_capabilities("postgres")
109 }
110
111 async fn execute_compiled_query(
112 &self,
113 compiled: &CompiledQuery,
114 ) -> Result<Vec<serde_json::Value>> {
115 execute_select_postgres(&self.pool, compiled, "").await
116 }
117
118 async fn ensure_schemaless_table(&self, table: &str) -> Result<()> {
119 ensure_table_postgres(&self.pool, table).await
120 }
121
122 async fn get_record(&self, table: &str, id: &str) -> Result<Option<serde_json::Value>> {
123 get_record_postgres(&self.pool, table, id).await
124 }
125
126 async fn create_record(
127 &self,
128 table: &str,
129 content: serde_json::Value,
130 ) -> Result<serde_json::Value> {
131 create_record_postgres(&self.pool, table, content).await
132 }
133
134 async fn update_record(
135 &self,
136 table: &str,
137 id: &str,
138 content: serde_json::Value,
139 ) -> Result<serde_json::Value> {
140 update_record_postgres(&self.pool, table, id, content).await
141 }
142
143 async fn merge_record(
144 &self,
145 table: &str,
146 id: &str,
147 patch: serde_json::Value,
148 ) -> Result<serde_json::Value> {
149 merge_record_postgres(&self.pool, table, id, patch).await
150 }
151
152 async fn upsert_record(
153 &self,
154 table: &str,
155 id: &str,
156 content: serde_json::Value,
157 ) -> Result<serde_json::Value> {
158 if self.get_record(table, id).await?.is_some() {
159 self.update_record(table, id, content).await
160 } else {
161 let mut c = content;
162 if let Some(obj) = c.as_object_mut() {
163 obj.insert("id".into(), serde_json::json!({"table": table, "id": id}));
164 }
165 self.create_record(table, c).await
166 }
167 }
168
169 async fn delete_record(&self, table: &str, id: &str) -> Result<()> {
170 delete_record_postgres(&self.pool, table, id).await
171 }
172
173 async fn relate_edge(&self, from: &RecordId, edge_table: &str, to: &RecordId) -> Result<()> {
174 relate_edge_postgres(&self.pool, from, edge_table, to).await
175 }
176
177 async fn unrelate_edge(&self, from: &RecordId, edge_table: &str, to: &RecordId) -> Result<()> {
178 unrelate_edge_postgres(&self.pool, from, edge_table, to).await
179 }
180
181 async fn get_edge_targets(&self, from: &RecordId, edge_table: &str) -> Result<Vec<RecordId>> {
182 get_edge_targets_postgres(&self.pool, from, edge_table).await
183 }
184
185 async fn get_edge_sources(&self, to: &RecordId, edge_table: &str) -> Result<Vec<RecordId>> {
186 get_edge_sources_postgres(&self.pool, to, edge_table).await
187 }
188
189 async fn define_unique_index(&self, table: &str, field: &str) -> Result<()> {
190 define_unique_index_postgres(&self.pool, table, field).await
191 }
192
193 fn ttl_capability(&self) -> valence_core::ttl::BackendTtlCapability {
194 ttl_deferred()
195 }
196
197 async fn apply_ttl_policy(&self, table: &str, policy: &SchemaTtlPolicy) -> Result<()> {
198 apply_ttl_policy_postgres(&self.pool, table, policy).await
199 }
200}