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