1use crate::database::{
23 ColumnDef, DataType, DatabaseConfig, DatabaseConnection, Index, QueryBuilder, QueryType,
24 ResultSet, TableSchema,
25};
26use crate::error::{IoError, Result};
27use oxisql_core::{Connection as OxiConnection, Row as OxiRow, ToSqlValue, Transaction, Value};
28use oxisql_sqlite_compat::SqliteConnection;
29use scirs2_core::ndarray::ArrayView2;
30use std::future::Future;
31use std::sync::Mutex;
32
33fn run_sync<F, T, E>(fut: F) -> std::result::Result<T, E>
41where
42 F: Future<Output = std::result::Result<T, E>>,
43{
44 match tokio::runtime::Handle::try_current() {
45 Ok(handle) => {
46 tokio::task::block_in_place(|| handle.block_on(fut))
50 }
51 Err(_) => {
52 let rt = tokio::runtime::Builder::new_current_thread()
54 .enable_all()
55 .build()
56 .map_err(|_| {
57 panic!("scirs2-io sqlite: failed to create tokio runtime for sync bridge")
62 })
63 .expect("tokio runtime creation cannot fail in practice");
64 rt.block_on(fut)
65 }
66 }
67}
68
69fn oxi_err(e: oxisql_core::OxiSqlError) -> IoError {
71 IoError::DatabaseError(e.to_string())
72}
73
74fn oxi_value_to_json(v: &Value) -> serde_json::Value {
76 match v {
77 Value::Null => serde_json::Value::Null,
78 Value::Bool(b) => serde_json::json!(*b),
79 Value::I64(n) => serde_json::json!(*n),
80 Value::F64(f) => serde_json::json!(*f),
81 Value::Text(s) => serde_json::json!(s),
82 Value::Blob(b) => serde_json::json!(crate::encoding_utils::base64_encode(b)),
83 Value::Timestamp(ts) => serde_json::json!(*ts),
84 Value::Date(d) => serde_json::json!(*d),
85 Value::Time(t) => serde_json::json!(*t),
86 Value::Uuid(u) => serde_json::json!(format!("{v}")),
87 Value::Json(j) => serde_json::from_str(j).unwrap_or_else(|_| serde_json::json!(j)),
88 Value::Decimal(d) => serde_json::json!(d),
89 Value::Array(arr) => serde_json::Value::Array(arr.iter().map(oxi_value_to_json).collect()),
90 Value::TypedArray { values, .. } => {
94 serde_json::Value::Array(values.iter().map(oxi_value_to_json).collect())
95 }
96 }
97}
98
99fn json_param_to_oxi(p: &serde_json::Value) -> Value {
101 match p {
102 serde_json::Value::String(s) => Value::Text(s.clone()),
103 serde_json::Value::Number(n) => {
104 if let Some(i) = n.as_i64() {
105 Value::I64(i)
106 } else {
107 Value::F64(n.as_f64().unwrap_or(0.0))
108 }
109 }
110 serde_json::Value::Bool(b) => Value::Bool(*b),
111 serde_json::Value::Null => Value::Null,
112 serde_json::Value::Array(_) | serde_json::Value::Object(_) => Value::Text(p.to_string()),
113 }
114}
115
116pub struct SQLiteConnection {
120 config: DatabaseConfig,
121 connection: Option<Mutex<SqliteConnection>>,
124}
125
126impl SQLiteConnection {
127 pub fn new(config: &DatabaseConfig) -> Result<Self> {
131 let conn = run_sync(SqliteConnection::open(&config.database)).map_err(oxi_err)?;
132
133 let _ = run_sync(conn.execute("PRAGMA foreign_keys = ON", &[]));
135
136 Ok(Self {
137 config: config.clone(),
138 connection: Some(Mutex::new(conn)),
139 })
140 }
141
142 fn with_conn<F, T>(&self, f: F) -> Result<T>
145 where
146 F: FnOnce(&SqliteConnection) -> Result<T>,
147 {
148 let guard = self
149 .connection
150 .as_ref()
151 .ok_or_else(|| IoError::DatabaseError("SQLite connection not initialised".to_string()))?
152 .lock()
153 .map_err(|_| IoError::DatabaseError("SQLite connection mutex poisoned".to_string()))?;
154 f(&guard)
155 }
156}
157
158impl DatabaseConnection for SQLiteConnection {
159 fn query(&self, query: &QueryBuilder) -> Result<ResultSet> {
160 let sql = query.build_sql();
161 let params = &query.values;
162 self.execute_sql(&sql, params)
163 }
164
165 fn execute_sql(&self, sql: &str, params: &[serde_json::Value]) -> Result<ResultSet> {
166 self.with_conn(|conn| {
167 let oxi_params: Vec<Value> = params.iter().map(json_param_to_oxi).collect();
169 let param_refs: Vec<&dyn ToSqlValue> =
170 oxi_params.iter().map(|v| v as &dyn ToSqlValue).collect();
171
172 let rows = run_sync(conn.query(sql, ¶m_refs)).map_err(oxi_err)?;
173
174 let column_names: Vec<String> = rows
176 .first()
177 .map(|r| r.columns().to_vec())
178 .unwrap_or_default();
179
180 let mut result = ResultSet::new(column_names.clone());
181
182 for row in &rows {
183 let col_count = row.column_count();
184 let mut row_data = Vec::with_capacity(col_count);
185 for i in 0..col_count {
186 let v = row
187 .get_by_index(i)
188 .map(oxi_value_to_json)
189 .unwrap_or(serde_json::Value::Null);
190 row_data.push(v);
191 }
192 result.add_row(row_data);
193 }
194
195 Ok(result)
196 })
197 }
198
199 fn insert_array(&self, table: &str, data: ArrayView2<f64>, columns: &[&str]) -> Result<usize> {
200 if columns.len() != data.ncols() {
201 return Err(IoError::ValidationError(
202 "Number of columns doesn't match data dimensions".to_string(),
203 ));
204 }
205
206 self.with_conn(|conn| {
207 let placeholders: Vec<String> =
208 (1..=columns.len()).map(|i| format!("${}", i)).collect();
209 let insert_sql = format!(
210 "INSERT INTO {} ({}) VALUES ({})",
211 table,
212 columns.join(", "),
213 placeholders.join(", ")
214 );
215
216 let mut txn = run_sync(conn.transaction()).map_err(oxi_err)?;
218
219 for row in data.rows() {
220 let row_vals: Vec<Value> = row.iter().map(|&f| Value::F64(f)).collect();
221 let row_refs: Vec<&dyn ToSqlValue> =
222 row_vals.iter().map(|v| v as &dyn ToSqlValue).collect();
223 run_sync(txn.execute(&insert_sql, &row_refs)).map_err(oxi_err)?;
224 }
225
226 run_sync(txn.commit()).map_err(oxi_err)?;
227 Ok(data.nrows())
229 })
230 }
231
232 fn create_table(&self, table: &str, schema: &TableSchema) -> Result<()> {
233 self.with_conn(|conn| {
234 let column_defs: Vec<String> = schema
235 .columns
236 .iter()
237 .map(|col| {
238 let sqlite_type = match col.data_type {
239 DataType::Integer | DataType::BigInt => "INTEGER",
240 DataType::Float | DataType::Double => "REAL",
241 DataType::Decimal(_, _) => "REAL",
242 DataType::Varchar(_) | DataType::Text => "TEXT",
243 DataType::Boolean => "INTEGER",
244 DataType::Date | DataType::Timestamp => "TEXT",
245 DataType::Json => "TEXT",
246 DataType::Binary => "BLOB",
247 };
248 let nullable = if col.nullable { "" } else { " NOT NULL" };
249 format!("{} {}{}", col.name, sqlite_type, nullable)
250 })
251 .collect();
252
253 let mut create_sql = format!("CREATE TABLE {} (", table);
254 create_sql.push_str(&column_defs.join(", "));
255
256 if let Some(ref pk_cols) = schema.primary_key {
257 create_sql.push_str(&format!(", PRIMARY KEY ({})", pk_cols.join(", ")));
258 }
259 create_sql.push(')');
260
261 run_sync(conn.execute(&create_sql, &[])).map_err(oxi_err)?;
262 Ok(())
263 })
264 }
265
266 fn table_exists(&self, table: &str) -> Result<bool> {
267 self.with_conn(|conn| {
268 let table_val = Value::Text(table.to_string());
269 let params: &[&dyn ToSqlValue] = &[&table_val];
270 let rows = run_sync(conn.query(
271 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=$1",
272 params,
273 ))
274 .map_err(oxi_err)?;
275
276 let count = rows
277 .first()
278 .and_then(|r| r.get_by_index(0))
279 .and_then(|v| {
280 if let Value::I64(n) = v {
281 Some(*n)
282 } else {
283 None
284 }
285 })
286 .unwrap_or(0);
287
288 Ok(count > 0)
289 })
290 }
291
292 fn get_schema(&self, table: &str) -> Result<TableSchema> {
293 self.with_conn(|conn| {
294 let sql = format!("PRAGMA table_info({})", table);
296 let rows = run_sync(conn.query(&sql, &[])).map_err(oxi_err)?;
297
298 let mut columns = Vec::new();
299 let mut primary_key: Vec<String> = Vec::new();
300
301 for row in &rows {
302 let name = row
303 .get_by_index(1)
304 .and_then(|v| {
305 if let Value::Text(s) = v {
306 Some(s.clone())
307 } else {
308 None
309 }
310 })
311 .unwrap_or_default();
312
313 let type_str = row
314 .get_by_index(2)
315 .and_then(|v| {
316 if let Value::Text(s) = v {
317 Some(s.clone())
318 } else {
319 None
320 }
321 })
322 .unwrap_or_default();
323
324 let notnull = row
325 .get_by_index(3)
326 .and_then(|v| {
327 if let Value::I64(n) = v {
328 Some(*n)
329 } else {
330 None
331 }
332 })
333 .unwrap_or(0);
334
335 let default_val = row.get_by_index(4).and_then(|v| match v {
336 Value::Text(s) => Some(serde_json::Value::String(s.clone())),
337 Value::Null => None,
338 other => Some(serde_json::json!(format!("{:?}", other))),
339 });
340
341 let pk_flag = row
342 .get_by_index(5)
343 .and_then(|v| {
344 if let Value::I64(n) = v {
345 Some(*n)
346 } else {
347 None
348 }
349 })
350 .unwrap_or(0);
351
352 let data_type = match type_str.to_uppercase().as_str() {
353 "INTEGER" => DataType::Integer,
354 "REAL" => DataType::Double,
355 "TEXT" => DataType::Text,
356 "BLOB" => DataType::Binary,
357 _ => DataType::Text,
358 };
359
360 columns.push(ColumnDef {
361 name: name.clone(),
362 data_type,
363 nullable: notnull == 0,
364 default: default_val,
365 });
366
367 if pk_flag > 0 {
368 primary_key.push(name);
369 }
370 }
371
372 Ok(TableSchema {
373 name: table.to_string(),
374 columns,
375 primary_key: if primary_key.is_empty() {
376 None
377 } else {
378 Some(primary_key)
379 },
380 indexes: Vec::new(),
381 })
382 })
383 }
384}