1use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions};
4use std::str::FromStr;
5
6use valence_backend_sql::{
7 create_record_sqlite, define_unique_index_sqlite, delete_record_sqlite, ensure_table_sqlite,
8 execute_select_sqlite, get_edge_targets_sqlite, get_record_sqlite, merge_record_sqlite,
9 relate_edge_sqlite, sql_capabilities, ttl_deferred, unrelate_edge_sqlite, update_record_sqlite,
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::SQLITE;
20
21pub const PRIMARY: DatabaseFromEngine = Database::from_engine("primary", ENGINE_ID);
23
24#[derive(Debug, Clone)]
61pub struct SqliteBackend {
62 pool: SqlitePool,
63}
64
65impl SqliteBackend {
66 pub async fn connect_memory() -> Result<Self> {
72 Self::connect(":memory:").await
73 }
74
75 pub async fn connect(path: &str) -> Result<Self> {
85 let options = SqliteConnectOptions::from_str(path)
86 .or_else(|_| SqliteConnectOptions::from_str(&format!("sqlite:{path}")))
87 .map_err(|e| Error::database(e.to_string()))?
88 .create_if_missing(true);
89 let memory =
90 path.contains(":memory:") || path.contains("mode=memory") || path == ":memory:";
91 let mut pool_opts = SqlitePoolOptions::new();
92 if memory {
93 pool_opts = pool_opts.max_connections(1);
94 }
95 let pool = pool_opts
96 .connect_with(options)
97 .await
98 .map_err(|e| Error::database(e.to_string()))?;
99 valence_backend_sql::ensure_edges_sqlite(&pool).await?;
100 Ok(Self { pool })
101 }
102
103 pub fn pool(&self) -> &SqlitePool {
105 &self.pool
106 }
107}
108
109#[async_trait::async_trait]
110impl DatabaseBackend for SqliteBackend {
111 fn engine_id(&self) -> &'static str {
112 ENGINE_ID
113 }
114
115 fn capabilities(&self) -> valence_core::BackendCapabilities {
116 sql_capabilities("sqlite")
117 }
118
119 async fn execute_compiled_query(
120 &self,
121 compiled: &CompiledQuery,
122 ) -> Result<Vec<serde_json::Value>> {
123 execute_select_sqlite(&self.pool, compiled, "").await
124 }
125
126 async fn ensure_schemaless_table(&self, table: &str) -> Result<()> {
127 ensure_table_sqlite(&self.pool, table).await
128 }
129
130 async fn get_record(&self, table: &str, id: &str) -> Result<Option<serde_json::Value>> {
131 get_record_sqlite(&self.pool, table, id).await
132 }
133
134 async fn create_record(
135 &self,
136 table: &str,
137 content: serde_json::Value,
138 ) -> Result<serde_json::Value> {
139 create_record_sqlite(&self.pool, table, content).await
140 }
141
142 async fn update_record(
143 &self,
144 table: &str,
145 id: &str,
146 content: serde_json::Value,
147 ) -> Result<serde_json::Value> {
148 update_record_sqlite(&self.pool, table, id, content).await
149 }
150
151 async fn merge_record(
152 &self,
153 table: &str,
154 id: &str,
155 patch: serde_json::Value,
156 ) -> Result<serde_json::Value> {
157 merge_record_sqlite(&self.pool, table, id, patch).await
158 }
159
160 async fn upsert_record(
161 &self,
162 table: &str,
163 id: &str,
164 content: serde_json::Value,
165 ) -> Result<serde_json::Value> {
166 if self.get_record(table, id).await?.is_some() {
167 self.update_record(table, id, content).await
168 } else {
169 let mut c = content;
170 if let Some(obj) = c.as_object_mut() {
171 obj.insert("id".into(), serde_json::json!({"table": table, "id": id}));
172 }
173 self.create_record(table, c).await
174 }
175 }
176
177 async fn delete_record(&self, table: &str, id: &str) -> Result<()> {
178 delete_record_sqlite(&self.pool, table, id).await
179 }
180
181 async fn relate_edge(&self, from: &RecordId, edge_table: &str, to: &RecordId) -> Result<()> {
182 relate_edge_sqlite(&self.pool, from, edge_table, to).await
183 }
184
185 async fn unrelate_edge(&self, from: &RecordId, edge_table: &str, to: &RecordId) -> Result<()> {
186 unrelate_edge_sqlite(&self.pool, from, edge_table, to).await
187 }
188
189 async fn get_edge_targets(&self, from: &RecordId, edge_table: &str) -> Result<Vec<RecordId>> {
190 get_edge_targets_sqlite(&self.pool, from, edge_table).await
191 }
192
193 async fn define_unique_index(&self, table: &str, field: &str) -> Result<()> {
194 define_unique_index_sqlite(&self.pool, table, field).await
195 }
196
197 fn ttl_capability(&self) -> valence_core::ttl::BackendTtlCapability {
198 ttl_deferred()
199 }
200
201 async fn apply_ttl_policy(&self, _table: &str, _policy: &SchemaTtlPolicy) -> Result<()> {
202 Ok(())
203 }
204}