1use std::{path::Path, sync::Arc};
2
3use sea_orm::{ConnectionTrait, Database, DbBackend};
4
5use crate::engine::{Dialect, Engine, sql_err};
6use crate::{ColumnKind, Name, Schema, Store, StoreError, Value};
7
8#[derive(Clone)]
10pub struct Sqlite;
11
12impl Dialect for Sqlite {
13 fn backend(&self) -> DbBackend {
14 DbBackend::Sqlite
15 }
16
17 fn mark(&self, _at: usize) -> String {
18 "?".into()
19 }
20
21 fn sql(&self, dtype: &str) -> &'static str {
22 match dtype {
23 "REAL" => "REAL",
24 "INTEGER" => "INTEGER",
25 _ => "TEXT",
26 }
27 }
28
29 fn create(
30 &self,
31 schema: &Schema,
32 _head: &[(Name, Value)],
33 columns: &str,
34 groups: &str,
35 ) -> String {
36 format!(
37 "INSERT OR REPLACE INTO \"{}\" ({columns}) VALUES {groups}",
38 schema.table
39 )
40 }
41
42 fn introspect(&self, table: &str) -> String {
43 format!("PRAGMA table_info(\"{table}\")")
44 }
45
46 fn name_at(&self) -> usize {
47 1
48 }
49
50 fn type_at(&self) -> usize {
51 2
52 }
53
54 fn reflect(&self, sql: &str) -> ColumnKind {
55 let sql = sql.to_uppercase();
56 if sql.contains("INT") {
57 ColumnKind::Integer
58 } else if sql.contains("CHAR") || sql.contains("CLOB") || sql.contains("TEXT") {
59 ColumnKind::Text
60 } else if sql.contains("REAL") || sql.contains("FLOA") || sql.contains("DOUB") {
61 ColumnKind::Real
62 } else {
63 ColumnKind::Text
64 }
65 }
66
67 fn latest(&self) -> Option<&'static str> {
68 Some("SELECT last_insert_rowid()")
69 }
70
71 fn sum(&self, _kind: ColumnKind, column: &str) -> String {
72 format!("SUM(\"{column}\")")
73 }
74
75 fn mean(&self, _kind: ColumnKind, column: &str) -> String {
76 format!("AVG(\"{column}\")")
77 }
78}
79
80pub async fn open(path: impl AsRef<Path>) -> Result<Arc<dyn Store>, StoreError> {
82 let url = format!("sqlite://{}?mode=rwc", path.as_ref().display());
83 let conn = Database::connect(&url).await.map_err(sql_err)?;
84 Ok(Arc::new(Engine::new(Sqlite, conn)))
85}
86
87pub async fn open_wal(path: impl AsRef<Path>) -> Result<Arc<dyn Store>, StoreError> {
89 let url = format!("sqlite://{}?mode=rwc", path.as_ref().display());
90 let conn = Database::connect(&url).await.map_err(sql_err)?;
91 for pragma in [
92 "PRAGMA journal_mode=WAL",
93 "PRAGMA synchronous=NORMAL",
94 "PRAGMA busy_timeout=5000",
95 ] {
96 conn.execute_unprepared(pragma).await.map_err(sql_err)?;
97 }
98 Ok(Arc::new(Engine::new(Sqlite, conn)))
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::engine::{alter, ddl, drop, kinds, rename};
105 use crate::{
106 Column, Field, Filter, Key, Mass, Name, Only, Op, Order, Page, Query, Sort, Table, Tree,
107 };
108 use chrono::{DateTime, NaiveDate, NaiveTime, Utc};
109 use rust_decimal::Decimal;
110
111 async fn open_db(name: &str) -> Arc<dyn Store> {
112 let path =
113 std::env::temp_dir().join(format!("rango-test-{}-{}.sqlite", std::process::id(), name));
114 let _ = std::fs::remove_file(&path);
115 open(&path).await.unwrap()
116 }
117
118 async fn store() -> Arc<dyn Store> {
119 open_db("base").await
120 }
121
122 fn schema() -> Schema {
123 Schema {
124 table: Table("w"),
125 fields: vec![
126 Field::id(),
127 Field::str("name"),
128 Field::cell::<i64>("age").optional(),
129 Field::cell::<DateTime<Utc>>("at").optional(),
130 ],
131 rules: Vec::new(),
132 }
133 }
134
135 fn col(name: &str, kind: ColumnKind) -> Column {
136 Column {
137 name: name.to_string(),
138 kind,
139 }
140 }
141
142 fn posts() -> Schema {
143 Schema {
144 table: Table("posts"),
145 fields: vec![Field::id(), Field::str("title")],
146 rules: Vec::new(),
147 }
148 }
149
150 fn keyed() -> Schema {
151 Schema {
152 table: Table("products"),
153 fields: vec![Field::key::<String>("sku"), Field::cell::<Decimal>("price")],
154 rules: Vec::new(),
155 }
156 }
157
158 #[test]
159 fn renders() {
160 assert_eq!(
161 ddl(&Sqlite, &posts()),
162 "CREATE TABLE IF NOT EXISTS \"posts\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT, \"title\" TEXT NOT NULL)"
163 );
164 assert_eq!(
165 ddl(&Sqlite, &keyed()),
166 "CREATE TABLE IF NOT EXISTS \"products\" (\"sku\" TEXT PRIMARY KEY, \"price\" TEXT NOT NULL)"
167 );
168 assert_eq!(kinds(&posts()), vec![ColumnKind::Integer, ColumnKind::Text]);
169 }
170
171 #[test]
172 fn alters() {
173 let schema = posts();
174 let have = vec![
175 col("id", ColumnKind::Integer),
176 col("title", ColumnKind::Text),
177 ];
178 assert!(alter(&Sqlite, &schema, &have).is_empty());
179 let missing = vec![col("id", ColumnKind::Integer)];
180 assert_eq!(alter(&Sqlite, &schema, &missing).len(), 1);
181 }
182
183 #[test]
184 fn drops() {
185 let schema = posts();
186 let have = vec![
187 col("id", ColumnKind::Integer),
188 col("title", ColumnKind::Text),
189 col("junk", ColumnKind::Text),
190 ];
191 assert_eq!(drop(&schema, &have).len(), 1);
192 assert!(drop(&schema, &have[..2]).is_empty());
193 }
194
195 #[test]
196 fn renames() {
197 let schema = posts();
198 let have = vec![
199 col("id", ColumnKind::Integer),
200 col("name", ColumnKind::Text),
201 ];
202 assert_eq!(rename(&schema, &have).len(), 1);
203 assert!(alter(&Sqlite, &schema, &have).is_empty());
204 assert!(drop(&schema, &have).is_empty());
205 let mixed = vec![
206 col("id", ColumnKind::Integer),
207 col("name", ColumnKind::Text),
208 col("age", ColumnKind::Integer),
209 ];
210 assert_eq!(rename(&schema, &mixed).len(), 1);
211 assert!(alter(&Sqlite, &schema, &mixed).is_empty());
212 assert_eq!(drop(&schema, &mixed).len(), 1);
213 }
214
215 #[tokio::test]
216 async fn upserts() {
217 let db = open_db("upserts").await;
218 let schema = Schema {
219 table: Table("p"),
220 fields: vec![Field::key::<String>("code"), Field::str("name")],
221 rules: Vec::new(),
222 };
223 db.define(&schema).await.unwrap();
224 let rows = vec![
225 vec![
226 (Name("code"), Value::str("x")),
227 (Name("name"), Value::str("n1")),
228 ],
229 vec![
230 (Name("code"), Value::str("x")),
231 (Name("name"), Value::str("n2")),
232 ],
233 vec![
234 (Name("code"), Value::str("y")),
235 (Name("name"), Value::str("m")),
236 ],
237 ];
238 db.create(&schema, &rows).await.unwrap();
239 let query = |api| async { db.total_query(&schema, &ask(api)).await.unwrap() };
240 assert_eq!(query(Tree::And(Vec::new())).await, 2);
241 let row = db
242 .scan_query(&schema, &ask(Tree::And(Vec::new())))
243 .await
244 .unwrap()
245 .into_iter()
246 .find(|row| row.str(0).unwrap() == "x")
247 .expect("x row");
248 assert_eq!(row.str(1).unwrap(), "n2");
249 }
250
251 #[tokio::test]
252 async fn upsert_merge() {
253 let db = open_db("upsert_merge").await;
254 let schema = Schema {
255 table: Table("p2"),
256 fields: vec![Field::key::<String>("code"), Field::str("name")],
257 rules: Vec::new(),
258 };
259 db.define(&schema).await.unwrap();
260 let one = vec![vec![
261 (Name("code"), Value::str("x")),
262 (Name("name"), Value::str("n1")),
263 ]];
264 assert_eq!(db.upsert(&schema, &one).await.unwrap(), 1);
265 let two = vec![
266 vec![
267 (Name("code"), Value::str("x")),
268 (Name("name"), Value::str("n2")),
269 ],
270 vec![
271 (Name("code"), Value::str("y")),
272 (Name("name"), Value::str("m")),
273 ],
274 ];
275 assert_eq!(db.upsert(&schema, &two).await.unwrap(), 2);
276 let query = |api| async { db.total_query(&schema, &ask(api)).await.unwrap() };
277 assert_eq!(query(Tree::And(Vec::new())).await, 2);
278 let row = db
279 .scan_query(&schema, &ask(Tree::And(Vec::new())))
280 .await
281 .unwrap()
282 .into_iter()
283 .find(|row| row.str(0).unwrap() == "x")
284 .expect("x row");
285 assert_eq!(row.str(1).unwrap(), "n2");
286 }
287
288 fn ask(tree: Tree) -> Query {
289 Query {
290 tree,
291 sort: Vec::new(),
292 page: Page::all(),
293 only: Only::All,
294 mass: None,
295 }
296 }
297
298 fn leaf(field: &'static str, op: Op, value: Value) -> Tree {
299 Tree::Leaf(Filter {
300 field: Name(field),
301 op,
302 value,
303 })
304 }
305
306 async fn seed() -> Arc<dyn Store> {
307 let db = open_db("trees").await;
308 db.define(&schema()).await.unwrap();
309 let day = NaiveDate::from_ymd_opt(2026, 1, 15)
310 .unwrap()
311 .and_time(NaiveTime::MIN)
312 .and_utc();
313 let rows = vec![
314 (Value::str("ann"), Value::int(30), Value::datetime(day)),
315 (Value::str("bob"), Value::int(40), Value::Null),
316 (Value::str("ann"), Value::int(30), Value::datetime(day)),
317 (Value::str("zed"), Value::Null, Value::Null),
318 ];
319 for (name, age, at) in rows {
320 db.execute(
321 "INSERT INTO w (name, age, at) VALUES (?, ?, ?)",
322 &[name, age, at],
323 )
324 .await
325 .unwrap();
326 }
327 db
328 }
329
330 #[tokio::test]
331 async fn trees() {
332 let db = seed().await;
333 let schema = schema();
334 let total = |tree: Tree| {
335 let db = db.clone();
336 let schema = schema.clone();
337 async move { db.total_query(&schema, &ask(tree)).await.unwrap() }
338 };
339 assert_eq!(total(Tree::And(Vec::new())).await, 4);
340 assert_eq!(total(leaf("name", Op::Eq, Value::str("ann"))).await, 2);
341 assert_eq!(total(leaf("name", Op::Like, Value::str("%ann%"))).await, 2);
342 assert_eq!(
343 total(Tree::Or(vec![
344 leaf("name", Op::Eq, Value::str("ann")),
345 leaf("age", Op::Eq, Value::int(40)),
346 ]))
347 .await,
348 3
349 );
350 assert_eq!(
351 total(Tree::Cut(Box::new(leaf("name", Op::Eq, Value::str("ann"))))).await,
352 2
353 );
354 assert_eq!(total(leaf("name", Op::Ne, Value::str("bob"))).await, 3);
355 assert_eq!(total(leaf("age", Op::More, Value::int(30))).await, 1);
356 assert_eq!(total(leaf("age", Op::Less, Value::int(40))).await, 2);
357 assert_eq!(total(leaf("age", Op::Bare, Value::Null)).await, 1);
358 let day = NaiveDate::from_ymd_opt(2026, 1, 15)
359 .unwrap()
360 .and_time(NaiveTime::MIN)
361 .and_utc();
362 assert_eq!(total(leaf("at", Op::At, Value::datetime(day))).await, 2);
363 let rows = db
364 .scan_query(
365 &schema,
366 &Query {
367 tree: Tree::And(Vec::new()),
368 sort: vec![Sort {
369 field: Name("age"),
370 order: Order::Desc,
371 }],
372 page: Page {
373 count: 2,
374 offset: 0,
375 },
376 only: Only::All,
377 mass: None,
378 },
379 )
380 .await
381 .unwrap();
382 assert_eq!(rows.len(), 2);
383 assert_eq!(rows[0].str(1).unwrap(), "bob");
384 let rows = db
385 .scan_query(
386 &schema,
387 &Query {
388 tree: Tree::And(Vec::new()),
389 sort: Vec::new(),
390 page: Page::all(),
391 only: Only::Some(vec![Name("name")]),
392 mass: None,
393 },
394 )
395 .await
396 .unwrap();
397 assert_eq!(rows.len(), 4);
398 assert!(rows.iter().all(|row| row.values.len() == 1));
399 assert_eq!(rows[0].str(0).unwrap(), "ann");
400 let rows = db
401 .scan_query(
402 &schema,
403 &Query {
404 tree: Tree::And(Vec::new()),
405 sort: vec![Sort {
406 field: Name("name"),
407 order: Order::Asc,
408 }],
409 page: Page::all(),
410 only: Only::Lone,
411 mass: None,
412 },
413 )
414 .await
415 .unwrap();
416 assert_eq!(rows.len(), 1);
417 assert_eq!(rows[0].str(1).unwrap(), "ann");
418 }
419
420 #[tokio::test]
421 async fn writes() {
422 let db = open_db("writes").await;
423 let schema = Schema {
424 table: Table("p"),
425 fields: vec![Field::id(), Field::str("name")],
426 rules: Vec::new(),
427 };
428 db.define(&schema).await.unwrap();
429 let keys = db
430 .create(
431 &schema,
432 &[
433 vec![(Name("name"), Value::str("a"))],
434 vec![(Name("name"), Value::str("b"))],
435 ],
436 )
437 .await
438 .unwrap();
439 assert_eq!(keys, vec![Key::Int(1), Key::Int(2)]);
440 let keyed = Schema {
441 table: Table("k"),
442 fields: vec![Field::key::<String>("sku"), Field::cell::<i64>("price")],
443 rules: Vec::new(),
444 };
445 db.define(&keyed).await.unwrap();
446 let keys = db
447 .create(
448 &keyed,
449 &[vec![
450 (Name("sku"), Value::str("s1")),
451 (Name("price"), Value::int(5)),
452 ]],
453 )
454 .await
455 .unwrap();
456 assert_eq!(keys, vec![Key::Text("s1".into())]);
457 db.replace(&schema, &Key::Int(1), &[(Name("name"), Value::str("a2"))])
458 .await
459 .unwrap();
460 let rows = db
461 .scan_query(&schema, &ask(Tree::And(Vec::new())))
462 .await
463 .unwrap();
464 assert_eq!(rows.len(), 2);
465 assert_eq!(rows[0].str(1).unwrap(), "a2");
466 db.remove(&schema, &Key::Int(2)).await.unwrap();
467 assert_eq!(
468 db.total_query(&schema, &ask(Tree::And(Vec::new())))
469 .await
470 .unwrap(),
471 1
472 );
473 }
474
475 #[tokio::test]
476 async fn evolves() {
477 let db = open_db("evolves").await;
478 let one = Schema {
479 table: Table("e"),
480 fields: vec![Field::id(), Field::str("name")],
481 rules: Vec::new(),
482 };
483 assert_eq!(db.evolve(&one, false).await.unwrap(), 1);
484 assert_eq!(db.evolve(&one, false).await.unwrap(), 1);
485 let two = Schema {
486 table: Table("e"),
487 fields: vec![
488 Field::id(),
489 Field::str("name"),
490 Field::cell::<i64>("age").optional(),
491 ],
492 rules: Vec::new(),
493 };
494 assert_eq!(db.evolve(&two, false).await.unwrap(), 2);
495 let names = db
496 .columns("e")
497 .await
498 .unwrap()
499 .into_iter()
500 .map(|col| col.name)
501 .collect::<Vec<_>>();
502 assert!(names.contains(&"age".to_string()));
503 assert_eq!(db.evolve(&one, false).await.unwrap(), 1);
504 assert_eq!(db.evolve(&one, true).await.unwrap(), 2);
505 let names = db
506 .columns("e")
507 .await
508 .unwrap()
509 .into_iter()
510 .map(|col| col.name)
511 .collect::<Vec<_>>();
512 assert!(!names.contains(&"age".to_string()));
513 }
514
515 #[tokio::test]
516 async fn masses() {
517 let db = open_db("masses").await;
518 let schema = Schema {
519 table: Table("g"),
520 fields: vec![
521 Field::id(),
522 Field::str("name"),
523 Field::cell::<i64>("age").optional(),
524 ],
525 rules: Vec::new(),
526 };
527 db.define(&schema).await.unwrap();
528 for (name, age) in [
529 (Value::str("ann"), Value::int(30)),
530 (Value::str("bob"), Value::int(40)),
531 (Value::str("cid"), Value::Null),
532 ] {
533 db.execute("INSERT INTO g (name, age) VALUES (?, ?)", &[name, age])
534 .await
535 .unwrap();
536 }
537 let mass = |mass: Mass, tree: Tree| {
538 let db = db.clone();
539 let schema = schema.clone();
540 async move {
541 db.mass(
542 &schema,
543 &Query {
544 tree,
545 sort: Vec::new(),
546 page: Page::all(),
547 only: Only::All,
548 mass: Some(mass),
549 },
550 )
551 .await
552 .unwrap()
553 }
554 };
555 assert_eq!(
556 mass(Mass::Count, Tree::And(Vec::new())).await,
557 Value::int(3)
558 );
559 assert_eq!(
560 mass(Mass::Count, leaf("name", Op::Eq, Value::str("ann"))).await,
561 Value::int(1)
562 );
563 assert_eq!(
564 mass(Mass::Sum(Name("age")), Tree::And(Vec::new())).await,
565 Value::int(70)
566 );
567 assert_eq!(
568 mass(Mass::Low(Name("age")), Tree::And(Vec::new())).await,
569 Value::int(30)
570 );
571 assert_eq!(
572 mass(Mass::High(Name("age")), Tree::And(Vec::new())).await,
573 Value::int(40)
574 );
575 match mass(Mass::Mean(Name("age")), Tree::And(Vec::new())).await {
576 Value::Float(mean) => assert!((mean - 35.0).abs() < 0.001),
577 _ => panic!("not a mean"),
578 }
579 }
580
581 #[tokio::test]
582 async fn roundtrip() {
583 let db = store().await;
584 db.execute(
585 "CREATE TABLE IF NOT EXISTS t (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, score REAL, flag INTEGER, at INTEGER)",
586 &[],
587 )
588 .await
589 .unwrap();
590 db.execute(
591 "INSERT INTO t (name, age, score, flag, at) VALUES (?, ?, ?, ?, ?)",
592 &[
593 Value::str("a"),
594 Value::int(1),
595 Value::float(1.5),
596 Value::bool(true),
597 Value::int(1700000001),
598 ],
599 )
600 .await
601 .unwrap();
602 assert_eq!(db.last_id("t").await.unwrap(), 1);
603 let kinds = [
604 ColumnKind::Integer,
605 ColumnKind::Text,
606 ColumnKind::Integer,
607 ColumnKind::Real,
608 ColumnKind::Integer,
609 ColumnKind::Integer,
610 ];
611 let rows = db
612 .fetch("SELECT * FROM t ORDER BY id", &[], &kinds)
613 .await
614 .unwrap();
615 assert_eq!(rows.len(), 1);
616 assert_eq!(rows[0].str(1).unwrap(), "a");
617 assert_eq!(rows[0].int(2).unwrap(), 1);
618 assert_eq!(rows[0].float(3).unwrap(), 1.5);
619 assert!(rows[0].bool(4).unwrap());
620 assert_eq!(rows[0].datetime(5).unwrap().timestamp(), 1700000001);
621 let cols = db.columns("t").await.unwrap();
622 assert_eq!(
623 cols.iter().map(|col| col.name.as_str()).collect::<Vec<_>>(),
624 vec!["id", "name", "age", "score", "flag", "at"]
625 );
626 let count = db
627 .fetch("SELECT COUNT(*) FROM t", &[], &[ColumnKind::Integer])
628 .await
629 .unwrap();
630 assert_eq!(count[0].int(0).unwrap(), 1);
631 let touched = db
632 .execute(
633 "UPDATE t SET age = ? WHERE id = ?",
634 &[Value::int(2), Value::int(1)],
635 )
636 .await
637 .unwrap();
638 assert_eq!(touched, 1);
639 let gone = db
640 .execute("DELETE FROM t WHERE id = ?", &[Value::int(1)])
641 .await
642 .unwrap();
643 assert_eq!(gone, 1);
644 }
645
646 #[tokio::test]
647 async fn missing() {
648 let db = store().await;
649 assert!(db.columns("nope").await.unwrap().is_empty());
650 assert!(db.fetch("SELECT * FROM nope", &[], &[]).await.is_err());
651 }
652
653 #[tokio::test]
654 async fn wipes() {
655 use crate::Action;
656 let db = open_db("wipes").await;
657 let schema = Schema {
658 table: Table("w"),
659 fields: vec![Field::id(), Field::str("name")],
660 rules: Vec::new(),
661 };
662 db.define(&schema).await.unwrap();
663 let keys = db
664 .create(
665 &schema,
666 &[
667 vec![(Name("name"), Value::str("a"))],
668 vec![(Name("name"), Value::str("b"))],
669 vec![(Name("name"), Value::str("c"))],
670 ],
671 )
672 .await
673 .unwrap();
674 let deed = Action::wipe();
675 assert_eq!(deed.name, Name("wipe"));
676 assert!(deed.logged);
677 let msg = (deed.run)(db.clone(), schema.clone(), keys[..2].to_vec())
678 .await
679 .unwrap();
680 assert_eq!(msg, "Deleted 2 rows.");
681 assert_eq!(
682 db.total_query(&schema, &ask(Tree::And(Vec::new())))
683 .await
684 .unwrap(),
685 1
686 );
687 let msg = (deed.run)(db.clone(), schema, keys[2..].to_vec())
688 .await
689 .unwrap();
690 assert_eq!(msg, "Deleted 1 row.");
691 }
692
693 fn deals() -> Schema {
694 Schema {
695 table: Table("deals"),
696 fields: vec![Field::id(), Field::str("name")],
697 rules: Vec::new(),
698 }
699 }
700
701 fn one() -> Vec<Vec<(Name, Value)>> {
702 vec![vec![(Name("name"), Value::str("a"))]]
703 }
704
705 #[tokio::test]
706 async fn commits() {
707 let db = open_db("commits").await;
708 let schema = deals();
709 db.define(&schema).await.unwrap();
710 let tx = db.deal().await.unwrap();
711 tx.create(&schema, &one()).await.unwrap();
712 assert_eq!(
713 tx.total_query(&schema, &ask(Tree::And(Vec::new())))
714 .await
715 .unwrap(),
716 1
717 );
718 tx.settle(true).await.unwrap();
719 assert_eq!(
720 db.total_query(&schema, &ask(Tree::And(Vec::new())))
721 .await
722 .unwrap(),
723 1
724 );
725 }
726
727 #[tokio::test]
728 async fn isolates() {
729 let path =
730 std::env::temp_dir().join(format!("rango-test-{}-isolates.sqlite", std::process::id()));
731 let _ = std::fs::remove_file(&path);
732 let first = open_wal(&path).await.unwrap();
733 let schema = deals();
734 first.define(&schema).await.unwrap();
735 let tx = first.deal().await.unwrap();
736 tx.create(&schema, &one()).await.unwrap();
737 let second = open_wal(&path).await.unwrap();
738 let ask_all = || ask(Tree::And(Vec::new()));
739 assert_eq!(second.total_query(&schema, &ask_all()).await.unwrap(), 0);
740 tx.settle(true).await.unwrap();
741 assert_eq!(second.total_query(&schema, &ask_all()).await.unwrap(), 1);
742 }
743
744 #[tokio::test]
745 async fn aborts() {
746 let db = open_db("aborts").await;
747 let schema = deals();
748 db.define(&schema).await.unwrap();
749 let tx = db.deal().await.unwrap();
750 tx.create(&schema, &one()).await.unwrap();
751 tx.settle(false).await.unwrap();
752 assert_eq!(
753 db.total_query(&schema, &ask(Tree::And(Vec::new())))
754 .await
755 .unwrap(),
756 0
757 );
758 }
759
760 #[tokio::test]
761 async fn nested() {
762 let db = open_db("nested").await;
763 let tx = db.deal().await.unwrap();
764 let err = match tx.deal().await {
765 Err(err) => err,
766 Ok(_) => panic!("nested deal"),
767 };
768 assert!(matches!(err, StoreError::Unsupported(_)));
769 tx.settle(false).await.unwrap();
770 }
771
772 #[tokio::test]
773 async fn abandons() {
774 let path =
775 std::env::temp_dir().join(format!("rango-test-{}-abandons.sqlite", std::process::id()));
776 let _ = std::fs::remove_file(&path);
777 let db = open_wal(&path).await.unwrap();
778 let schema = deals();
779 db.define(&schema).await.unwrap();
780 {
781 let tx = db.deal().await.unwrap();
782 tx.create(&schema, &one()).await.unwrap();
783 }
784 assert_eq!(
785 db.total_query(&schema, &ask(Tree::And(Vec::new())))
786 .await
787 .unwrap(),
788 0
789 );
790 }
791}