1use crate::error::{KitError, Result};
4use crate::internal::{ensure_internal_tables, internal_tables_core};
5use crate::schema::to_core_schema;
6use mongreldb_core::epoch::Snapshot;
7use mongreldb_core::memtable::Row as CoreRow;
8use mongreldb_core::memtable::Value as CoreValue;
9use mongreldb_core::schema::Schema as CoreSchema;
10use mongreldb_core::Database as CoreDatabase;
11use mongreldb_core::{AggState, ApproxAgg, NativeAgg, NativeAggResult, RowId};
12use mongreldb_kit_core::schema::IndexKind as KitIndexKind;
13use mongreldb_kit_core::schema::Schema as KitSchema;
14use mongreldb_kit_core::schema::Table as KitTable;
15use mongreldb_kit_core::{ProcedureSpec, TriggerSpec, ViewSpec};
16use serde_json::Value;
17
18use std::collections::HashMap;
19use std::path::{Path, PathBuf};
20use std::sync::Arc;
21
22const SCHEMA_FILE: &str = "kit_schema.json";
23
24pub type DefaultProvider = Box<dyn Fn() -> Value + Send + Sync>;
26
27#[derive(Debug, Clone)]
30pub struct ExplainPlan {
31 pub index_accelerated: bool,
33 pub exact: bool,
36 pub pushed_conditions: Vec<String>,
38}
39
40#[derive(Debug, Clone)]
42pub struct SimilarRow {
43 pub row: crate::schema::Row,
44 pub similarity: f64,
45}
46
47fn parse_string_set(value: Option<&Value>) -> std::collections::HashSet<String> {
51 let arr = match value {
52 Some(Value::Array(a)) => Some(a.clone()),
53 Some(Value::String(s)) => serde_json::from_str::<Value>(s)
54 .ok()
55 .and_then(|v| v.as_array().cloned()),
56 _ => None,
57 };
58 arr.into_iter()
59 .flatten()
60 .filter_map(|v| match v {
61 Value::String(s) => Some(s),
62 Value::Number(n) => Some(n.to_string()),
63 Value::Bool(b) => Some(b.to_string()),
64 _ => None,
65 })
66 .collect()
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum IncrementalAggKind {
72 Count,
73 Sum,
74 Min,
75 Max,
76 Avg,
77}
78
79#[derive(Debug, Clone)]
81pub struct IncrementalAggregate {
82 pub value: Value,
85 pub incremental: bool,
89 pub delta_rows: u64,
91}
92
93fn incremental_cache_key(
97 table_id: u32,
98 column: Option<u16>,
99 agg: IncrementalAggKind,
100 conditions: &[mongreldb_core::query::Condition],
101) -> u64 {
102 use std::hash::{Hash, Hasher};
103 let mut h = std::collections::hash_map::DefaultHasher::new();
104 table_id.hash(&mut h);
105 column.hash(&mut h);
106 (agg as u8).hash(&mut h);
107 format!("{conditions:?}").hash(&mut h);
109 h.finish()
110}
111
112fn agg_state_value(s: &AggState) -> Value {
116 let num_f64 = |x: f64| {
117 serde_json::Number::from_f64(x)
118 .map(Value::Number)
119 .unwrap_or(Value::Null)
120 };
121 match s {
122 AggState::Count(n) => Value::from(*n),
123 AggState::SumI { sum, .. } => i64::try_from(*sum)
124 .map(Value::from)
125 .unwrap_or_else(|_| num_f64(*sum as f64)),
126 AggState::SumF { sum, .. } => num_f64(*sum),
127 AggState::AvgI { sum, count } if *count > 0 => num_f64(*sum as f64 / *count as f64),
128 AggState::AvgF { sum, count } if *count > 0 => num_f64(*sum / *count as f64),
129 AggState::AvgI { .. } | AggState::AvgF { .. } => Value::Null,
130 AggState::MinI(n) | AggState::MaxI(n) => Value::from(*n),
131 AggState::MinF(f) | AggState::MaxF(f) => num_f64(*f),
132 AggState::Empty => Value::Null,
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum ApproxAggKind {
139 Count,
140 Sum,
141 Avg,
142}
143
144#[derive(Debug, Clone)]
148pub struct ApproxAggregate {
149 pub point: f64,
150 pub ci_low: f64,
151 pub ci_high: f64,
152 pub n_population: u64,
153 pub n_sample_live: usize,
154 pub n_passing: usize,
155}
156
157fn condition_label(c: &mongreldb_core::query::Condition) -> String {
160 let dbg = format!("{c:?}");
161 dbg.split(['(', '{', ' ']).next().unwrap_or("").to_string()
162}
163
164pub struct Database {
169 pub(crate) inner: Arc<CoreDatabase>,
170 pub(crate) schema: KitSchema,
171 pub(crate) root: PathBuf,
172 pub(crate) default_providers: HashMap<String, DefaultProvider>,
174 pub(crate) session: parking_lot::Mutex<Option<mongreldb_query::MongrelSession>>,
181}
182
183impl Database {
184 pub fn open(path: &Path) -> Result<Self> {
186 let inner = Arc::new(CoreDatabase::open(path)?);
187 let schema = load_schema(path)?;
188 ensure_internal_tables(&inner)?;
190 reap_rotated_wal_segments(&inner);
191 Ok(Self {
192 inner,
193 schema,
194 root: path.to_path_buf(),
195 default_providers: HashMap::new(),
196 session: parking_lot::Mutex::new(None),
197 })
198 }
199
200 pub fn open_encrypted(path: &Path, passphrase: &str) -> Result<Self> {
202 let inner = Arc::new(CoreDatabase::open_encrypted(path, passphrase)?);
203 let schema = load_schema(path)?;
204 ensure_internal_tables(&inner)?;
205 reap_rotated_wal_segments(&inner);
206 Ok(Self {
207 inner,
208 schema,
209 root: path.to_path_buf(),
210 default_providers: HashMap::new(),
211 session: parking_lot::Mutex::new(None),
212 })
213 }
214
215 pub fn create_encrypted(path: &Path, schema: KitSchema, passphrase: &str) -> Result<Self> {
219 std::fs::create_dir_all(path)?;
220 let inner = Arc::new(CoreDatabase::create_encrypted(path, passphrase)?);
221 ensure_internal_tables(&inner)?;
222 store_schema(path, &schema)?;
223 for table in &schema.tables {
224 create_core_table(&inner, &table.name, to_core_schema(table))?;
225 }
226 Ok(Self {
227 inner,
228 schema,
229 root: path.to_path_buf(),
230 default_providers: HashMap::new(),
231 session: parking_lot::Mutex::new(None),
232 })
233 }
234
235 pub fn create(path: &Path, schema: KitSchema) -> Result<Self> {
237 std::fs::create_dir_all(path)?;
238 let inner = Arc::new(CoreDatabase::create(path)?);
239
240 ensure_internal_tables(&inner)?;
243
244 store_schema(path, &schema)?;
247
248 for table in &schema.tables {
250 create_core_table(&inner, &table.name, to_core_schema(table))?;
251 }
252
253 Ok(Self {
254 inner,
255 schema,
256 root: path.to_path_buf(),
257 default_providers: HashMap::new(),
258 session: parking_lot::Mutex::new(None),
259 })
260 }
261
262 pub fn open_with_credentials(path: &Path, username: &str, password: &str) -> Result<Self> {
272 let inner = Arc::new(CoreDatabase::open_with_credentials(
273 path, username, password,
274 )?);
275 let schema = load_schema(path)?;
276 ensure_internal_tables(&inner)?;
277 reap_rotated_wal_segments(&inner);
278 Ok(Self {
279 inner,
280 schema,
281 root: path.to_path_buf(),
282 default_providers: HashMap::new(),
283 session: parking_lot::Mutex::new(None),
284 })
285 }
286
287 pub fn create_with_credentials(
293 path: &Path,
294 schema: KitSchema,
295 admin_username: &str,
296 admin_password: &str,
297 ) -> Result<Self> {
298 std::fs::create_dir_all(path)?;
299 let inner = Arc::new(CoreDatabase::create_with_credentials(
300 path,
301 admin_username,
302 admin_password,
303 )?);
304 ensure_internal_tables(&inner)?;
305 store_schema(path, &schema)?;
306 for table in &schema.tables {
307 create_core_table(&inner, &table.name, to_core_schema(table))?;
308 }
309 Ok(Self {
310 inner,
311 schema,
312 root: path.to_path_buf(),
313 default_providers: HashMap::new(),
314 session: parking_lot::Mutex::new(None),
315 })
316 }
317
318 pub fn open_encrypted_with_credentials(
321 path: &Path,
322 passphrase: &str,
323 username: &str,
324 password: &str,
325 ) -> Result<Self> {
326 let inner = Arc::new(CoreDatabase::open_encrypted_with_credentials(
327 path, passphrase, username, password,
328 )?);
329 let schema = load_schema(path)?;
330 ensure_internal_tables(&inner)?;
331 reap_rotated_wal_segments(&inner);
332 Ok(Self {
333 inner,
334 schema,
335 root: path.to_path_buf(),
336 default_providers: HashMap::new(),
337 session: parking_lot::Mutex::new(None),
338 })
339 }
340
341 pub fn create_encrypted_with_credentials(
345 path: &Path,
346 schema: KitSchema,
347 passphrase: &str,
348 admin_username: &str,
349 admin_password: &str,
350 ) -> Result<Self> {
351 std::fs::create_dir_all(path)?;
352 let inner = Arc::new(CoreDatabase::create_encrypted_with_credentials(
353 path,
354 passphrase,
355 admin_username,
356 admin_password,
357 )?);
358 ensure_internal_tables(&inner)?;
359 store_schema(path, &schema)?;
360 for table in &schema.tables {
361 create_core_table(&inner, &table.name, to_core_schema(table))?;
362 }
363 Ok(Self {
364 inner,
365 schema,
366 root: path.to_path_buf(),
367 default_providers: HashMap::new(),
368 session: parking_lot::Mutex::new(None),
369 })
370 }
371
372 pub fn enable_auth(&self, admin_username: &str, admin_password: &str) -> Result<()> {
376 self.inner
377 .enable_auth(admin_username, admin_password)
378 .map_err(KitError::from)
379 }
380
381 pub fn require_auth_enabled(&self) -> bool {
383 self.inner.require_auth_enabled()
384 }
385
386 pub fn refresh_principal(&self) -> Result<()> {
390 self.inner.refresh_principal().map_err(KitError::from)
391 }
392
393 pub fn register_default(
396 &mut self,
397 name: impl Into<String>,
398 provider: impl Fn() -> Value + Send + Sync + 'static,
399 ) {
400 self.default_providers
401 .insert(name.into(), Box::new(provider));
402 }
403
404 pub fn raw(&self) -> &CoreDatabase {
408 &self.inner
409 }
410
411 pub fn table_names(&self) -> Vec<String> {
413 self.schema
414 .tables
415 .iter()
416 .map(|t| t.name.clone())
417 .filter(|n| !n.starts_with("__kit_"))
418 .collect()
419 }
420
421 pub fn create_procedure(
422 &self,
423 spec: &ProcedureSpec,
424 ) -> Result<mongreldb_core::StoredProcedure> {
425 let procedure = core_procedure(spec)?;
426 self.inner
427 .create_procedure(procedure)
428 .map_err(KitError::from)
429 }
430
431 pub fn replace_procedure(
432 &self,
433 spec: &ProcedureSpec,
434 ) -> Result<mongreldb_core::StoredProcedure> {
435 let procedure = core_procedure(spec)?;
436 self.inner
437 .create_or_replace_procedure(procedure)
438 .map_err(KitError::from)
439 }
440
441 pub fn drop_procedure(&self, name: &str) -> Result<()> {
442 self.inner.drop_procedure(name).map_err(KitError::from)
443 }
444
445 pub fn call_procedure(
446 &self,
447 name: &str,
448 args: serde_json::Map<String, Value>,
449 ) -> Result<mongreldb_core::ProcedureCallResult> {
450 let args = args
451 .iter()
452 .map(|(key, value)| Ok((key.clone(), json_to_core_value(value)?)))
453 .collect::<Result<HashMap<_, _>>>()?;
454 self.inner
455 .call_procedure(name, args)
456 .map_err(KitError::from)
457 }
458
459 pub fn create_trigger(&self, spec: &TriggerSpec) -> Result<mongreldb_core::StoredTrigger> {
460 let trigger = core_trigger(spec)?;
461 self.inner.create_trigger(trigger).map_err(KitError::from)
462 }
463
464 pub fn replace_trigger(&self, spec: &TriggerSpec) -> Result<mongreldb_core::StoredTrigger> {
465 let trigger = core_trigger(spec)?;
466 self.inner
467 .create_or_replace_trigger(trigger)
468 .map_err(KitError::from)
469 }
470
471 pub fn drop_trigger(&self, name: &str) -> Result<()> {
472 self.inner.drop_trigger(name).map_err(KitError::from)
473 }
474
475 pub fn triggers(&self) -> Vec<mongreldb_core::StoredTrigger> {
476 self.inner.triggers()
477 }
478
479 pub fn trigger(&self, name: &str) -> Option<mongreldb_core::StoredTrigger> {
480 self.inner.trigger(name)
481 }
482
483 pub fn allocate_sequence(&self, name: &str, count: i64) -> Result<i64> {
489 use crate::internal::cols;
490 let mut attempt = 0;
491 loop {
492 let mut txn = self.inner.begin();
493 let snapshot = txn.read_snapshot();
494 let existing = self
495 .visible_core_rows_at(crate::internal::SEQUENCES, snapshot)?
496 .into_iter()
497 .find(|r| internal_bytes(r, cols::SEQ_NAME) == Some(name.to_string()));
498
499 let now = crate::internal::iso_now();
500 let (start, next, old_row_id) = match &existing {
504 Some(row) => {
505 let current = match row.columns.get(&cols::SEQ_NEXT) {
506 Some(CoreValue::Int64(i)) => *i,
507 _ => 1,
508 };
509 (current, current + count, Some(row.row_id))
510 }
511 None => (1, 1 + count, None),
512 };
513
514 if let Some(rid) = old_row_id {
515 txn.delete(crate::internal::SEQUENCES, rid)
516 .map_err(KitError::from)?;
517 }
518 txn.put(
519 crate::internal::SEQUENCES,
520 vec![
521 (cols::SEQ_NAME, CoreValue::Bytes(name.as_bytes().to_vec())),
522 (cols::SEQ_NEXT, CoreValue::Int64(next)),
523 (cols::SEQ_UPDATED, CoreValue::Bytes(now.into_bytes())),
524 ],
525 )
526 .map_err(KitError::from)?;
527 match txn.commit() {
528 Ok(_) => return Ok(start),
529 Err(mongreldb_core::MongrelError::Conflict(_)) if attempt < 10_000 => {
530 attempt += 1;
531 std::thread::yield_now();
532 continue;
533 }
534 Err(e) => return Err(KitError::from(e)),
535 }
536 }
537 }
538
539 pub fn transaction<T, F>(&self, max_retries: usize, mut f: F) -> Result<T>
542 where
543 F: FnMut(&mut crate::txn::Transaction<'_>) -> Result<T>,
544 {
545 let mut attempt = 0;
546 loop {
547 let mut txn = self.begin()?;
548 match f(&mut txn) {
549 Ok(value) => match txn.commit() {
550 Ok(()) => return Ok(value),
551 Err(KitError::Conflict(_)) if attempt < max_retries => {
552 attempt += 1;
553 continue;
554 }
555 Err(e) => return Err(e),
556 },
557 Err(KitError::Conflict(_)) if attempt < max_retries => {
558 txn.rollback();
559 attempt += 1;
560 continue;
561 }
562 Err(e) => {
563 txn.rollback();
564 return Err(e);
565 }
566 }
567 }
568 }
569
570 pub fn table(&self, name: &str) -> Option<&KitTable> {
572 self.schema.table(name)
573 }
574
575 pub fn schema(&self) -> &KitSchema {
577 &self.schema
578 }
579
580 pub fn begin(&self) -> Result<crate::txn::Transaction<'_>> {
582 let core_txn = self.inner.begin();
583 Ok(crate::txn::Transaction::new(self, core_txn))
584 }
585
586 pub fn set_schema(&mut self, schema: KitSchema) {
588 self.schema = schema;
589 }
590
591 pub fn check_internal_tables(&self) -> Result<()> {
594 let schema_file = self.root.join(SCHEMA_FILE);
595 if !schema_file.exists() {
596 return Err(KitError::Integrity(format!(
597 "schema file {} is missing",
598 schema_file.display()
599 )));
600 }
601 for (name, _) in internal_tables_core() {
602 if self.inner.table_id(name).is_err() {
603 return Err(KitError::Integrity(format!(
604 "internal table {name} is missing"
605 )));
606 }
607 }
608 Ok(())
609 }
610
611 pub fn gc(&self) -> Result<usize> {
614 self.inner.gc().map_err(KitError::from)
615 }
616
617 pub fn check(&self) -> Vec<serde_json::Value> {
620 self.inner
621 .check()
622 .into_iter()
623 .map(|i| {
624 serde_json::json!({
625 "table_id": i.table_id,
626 "table_name": i.table_name,
627 "severity": i.severity,
628 "description": i.description,
629 })
630 })
631 .collect()
632 }
633
634 pub fn doctor(&self) -> Result<Vec<u64>> {
636 self.inner.doctor().map_err(KitError::from)
637 }
638
639 pub fn snapshot_epoch(&self) -> u64 {
643 self.inner.snapshot().0.epoch.0
644 }
645
646 pub fn export_tsv(&self, table: &str) -> Result<String> {
650 let t = self
651 .schema
652 .tables
653 .iter()
654 .find(|t| t.name == table)
655 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?
656 .clone();
657 let tx = self.begin()?;
658 let rows = tx.all_rows(table)?;
659 Ok(crate::tsv::rows_to_tsv(&t, &rows))
660 }
661
662 pub fn import_tsv(&self, table: &str, text: &str) -> Result<usize> {
666 let t = self
667 .schema
668 .tables
669 .iter()
670 .find(|t| t.name == table)
671 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?
672 .clone();
673 let rows = crate::tsv::tsv_to_rows(&t, text)?;
674 let n = rows.len();
675 self.transaction(1, |tx| {
676 tx.insert_many(table, rows.clone())?;
677 Ok(())
678 })?;
679 Ok(n)
680 }
681
682 pub fn explain(
687 &self,
688 table: &str,
689 predicate: &mongreldb_kit_core::query::Expr,
690 ) -> Result<ExplainPlan> {
691 let t = self
692 .schema
693 .tables
694 .iter()
695 .find(|t| t.name == table)
696 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?;
697 Ok(match crate::pushdown::translate_predicate(t, predicate) {
698 Some(p) => ExplainPlan {
699 index_accelerated: p.can_push(),
700 exact: p.fully_translated,
701 pushed_conditions: p.conditions.iter().map(condition_label).collect(),
702 },
703 None => ExplainPlan {
704 index_accelerated: false,
705 exact: false,
706 pushed_conditions: Vec::new(),
707 },
708 })
709 }
710
711 pub fn rows_at_epoch(&self, table: &str, epoch: u64) -> Result<Vec<crate::schema::Row>> {
717 let t = self
718 .schema
719 .tables
720 .iter()
721 .find(|t| t.name == table)
722 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?;
723 let current = self.snapshot_epoch();
724 if epoch > current {
725 return Err(KitError::Validation(format!(
726 "epoch {epoch} is in the future (current committed epoch is {current})"
727 )));
728 }
729 let snap = Snapshot::at(mongreldb_core::epoch::Epoch(epoch));
730 let rows = self.visible_core_rows_at(table, snap)?;
731 rows.iter()
732 .map(|r| crate::schema::core_row_to_json(r, t))
733 .collect()
734 }
735
736 pub fn approx_aggregate(
742 &self,
743 table: &str,
744 column: Option<&str>,
745 agg: ApproxAggKind,
746 z: f64,
747 ) -> Result<Option<ApproxAggregate>> {
748 let t = self
749 .schema
750 .tables
751 .iter()
752 .find(|t| t.name == table)
753 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?;
754 if matches!(agg, ApproxAggKind::Sum | ApproxAggKind::Avg) && column.is_none() {
755 return Err(KitError::Validation(
756 "approx sum/avg requires a column".into(),
757 ));
758 }
759 let cid = match column {
760 Some(name) => Some(
761 t.columns
762 .iter()
763 .find(|c| c.name == name)
764 .ok_or_else(|| KitError::Validation(format!("unknown column '{name}'")))?
765 .id as u16,
766 ),
767 None => None,
768 };
769 let core_agg = match agg {
770 ApproxAggKind::Count => ApproxAgg::Count,
771 ApproxAggKind::Sum => ApproxAgg::Sum,
772 ApproxAggKind::Avg => ApproxAgg::Avg,
773 };
774 let handle = self.inner.table(table).map_err(KitError::from)?;
775 let mut guard = handle.lock();
776 let res = guard
777 .approx_aggregate(&[], cid, core_agg, z)
778 .map_err(KitError::from)?;
779 Ok(res.map(|r| ApproxAggregate {
780 point: r.point,
781 ci_low: r.ci_low,
782 ci_high: r.ci_high,
783 n_population: r.n_population,
784 n_sample_live: r.n_sample_live,
785 n_passing: r.n_passing,
786 }))
787 }
788
789 pub fn scan_batched<F>(&self, table: &str, batch_size: usize, mut f: F) -> Result<()>
795 where
796 F: FnMut(&[serde_json::Map<String, Value>]) -> Result<()>,
797 {
798 let kit_t = self
799 .schema
800 .tables
801 .iter()
802 .find(|t| t.name == table)
803 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?;
804 let batch_size = batch_size.max(1);
805 let (snapshot, _pin) = self.inner.snapshot();
808 let handle = self.inner.table(table).map_err(KitError::from)?;
809 let guard = handle.lock();
810
811 let mut projection: Vec<(u16, mongreldb_core::schema::TypeId)> = Vec::new();
813 let mut meta: Vec<(String, mongreldb_kit_core::schema::ColumnType)> = Vec::new();
814 for c in &guard.schema().columns {
815 if let Some(kc) = kit_t.columns.iter().find(|kc| kc.id as u16 == c.id) {
816 projection.push((c.id, c.ty));
817 meta.push((kc.name.clone(), kc.storage_type));
818 }
819 }
820
821 match guard
822 .scan_cursor(snapshot, projection, &[])
823 .map_err(KitError::from)?
824 {
825 Some(mut cursor) => {
826 let mut buf: Vec<serde_json::Map<String, Value>> = Vec::with_capacity(batch_size);
827 while let Some(batch) = cursor.next_batch().map_err(KitError::from)? {
828 let nrows = batch.first().map(|c| c.len()).unwrap_or(0);
829 for j in 0..nrows {
830 let mut m = serde_json::Map::new();
831 for (ci, (name, ty)) in meta.iter().enumerate() {
832 let cv = batch
833 .get(ci)
834 .and_then(|col| col.value_at(j))
835 .unwrap_or(CoreValue::Null);
836 m.insert(name.clone(), crate::schema::core_to_json(&cv, *ty)?);
837 }
838 buf.push(m);
839 if buf.len() >= batch_size {
840 f(&buf)?;
841 buf.clear();
842 }
843 }
844 }
845 if !buf.is_empty() {
846 f(&buf)?;
847 }
848 Ok(())
849 }
850 None => {
851 drop(guard);
852 let rows = self.visible_core_rows_at(table, snapshot)?;
853 let maps: Vec<serde_json::Map<String, Value>> = rows
854 .iter()
855 .map(|r| crate::schema::core_row_to_json(r, kit_t).map(|row| row.values))
856 .collect::<Result<Vec<_>>>()?;
857 for chunk in maps.chunks(batch_size) {
858 f(chunk)?;
859 }
860 Ok(())
861 }
862 }
863 }
864
865 pub fn set_similarity(
874 &self,
875 table: &str,
876 column: &str,
877 query: &[String],
878 k: usize,
879 ) -> Result<Vec<SimilarRow>> {
880 let t = self
881 .schema
882 .tables
883 .iter()
884 .find(|t| t.name == table)
885 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?;
886 let col = t.columns.iter().find(|c| c.name == column).ok_or_else(|| {
887 KitError::Validation(format!("unknown column '{column}' on table '{table}'"))
888 })?;
889 let query_set: std::collections::HashSet<String> = query.iter().cloned().collect();
890
891 let has_minhash = t.indexes.iter().any(|idx| {
892 idx.kind == KitIndexKind::MinHash && idx.columns.iter().any(|c| c == column)
893 });
894 let rows = if has_minhash {
895 let query_hashes: Vec<u64> = query
897 .iter()
898 .map(|s| mongreldb_core::index::minhash_token_hash(s))
899 .collect();
900 let cand_k = k.saturating_mul(8).max(k + 64);
902 let cond = mongreldb_core::query::Condition::MinHashSimilar {
903 column_id: col.id as u16,
904 query: query_hashes,
905 k: cand_k,
906 };
907 let (snapshot, _pin) = self.inner.snapshot();
908 let core_rows = self.query_core_rows_at(table, &[cond], snapshot)?;
909 core_rows
910 .iter()
911 .map(|r| crate::schema::core_row_to_json(r, t))
912 .collect::<Result<Vec<_>>>()?
913 } else {
914 let tx = self.begin()?;
915 tx.all_rows(table)?
916 };
917
918 let mut scored: Vec<SimilarRow> = Vec::new();
919 for row in rows {
920 let set = parse_string_set(row.values.get(column));
921 let inter = set.iter().filter(|x| query_set.contains(*x)).count();
922 let union = set.len() + query_set.len() - inter;
923 let sim = if union == 0 {
924 0.0
925 } else {
926 inter as f64 / union as f64
927 };
928 if sim > 0.0 {
929 scored.push(SimilarRow {
930 row,
931 similarity: sim,
932 });
933 }
934 }
935 scored.sort_by(|a, b| {
936 b.similarity
937 .partial_cmp(&a.similarity)
938 .unwrap_or(std::cmp::Ordering::Equal)
939 });
940 scored.truncate(k);
941 Ok(scored)
942 }
943
944 pub fn flush(&self) -> Result<()> {
948 for name in self.inner.table_names() {
949 let handle = self.inner.table(&name).map_err(KitError::from)?;
950 let mut guard = handle.lock();
951 guard.flush().map_err(KitError::from)?;
952 }
953 Ok(())
954 }
955
956 pub fn incremental_aggregate(
968 &self,
969 table: &str,
970 column: Option<&str>,
971 agg: IncrementalAggKind,
972 filter: Option<&mongreldb_kit_core::query::Expr>,
973 ) -> Result<IncrementalAggregate> {
974 let t = self
975 .schema
976 .tables
977 .iter()
978 .find(|t| t.name == table)
979 .ok_or_else(|| KitError::Validation(format!("unknown table '{table}'")))?;
980 if !matches!(agg, IncrementalAggKind::Count) && column.is_none() {
981 return Err(KitError::Validation(
982 "sum/min/max/avg incremental aggregate requires a column".into(),
983 ));
984 }
985 let cid = match column {
986 Some(name) => Some(
987 t.columns
988 .iter()
989 .find(|c| c.name == name)
990 .ok_or_else(|| KitError::Validation(format!("unknown column '{name}'")))?
991 .id as u16,
992 ),
993 None => None,
994 };
995 let conditions = match filter {
996 Some(expr) => {
997 let plan = crate::pushdown::translate_predicate(t, expr).ok_or_else(|| {
998 KitError::Validation(
999 "filter is not index-translatable for an incremental aggregate".into(),
1000 )
1001 })?;
1002 if !plan.fully_translated {
1003 return Err(KitError::Validation(
1004 "filter has a residual that an incremental aggregate cannot apply exactly"
1005 .into(),
1006 ));
1007 }
1008 plan.conditions
1009 }
1010 None => Vec::new(),
1011 };
1012 let core_agg = match agg {
1013 IncrementalAggKind::Count => NativeAgg::Count,
1014 IncrementalAggKind::Sum => NativeAgg::Sum,
1015 IncrementalAggKind::Min => NativeAgg::Min,
1016 IncrementalAggKind::Max => NativeAgg::Max,
1017 IncrementalAggKind::Avg => NativeAgg::Avg,
1018 };
1019 let cache_key = incremental_cache_key(t.id, cid, agg, &conditions);
1020 let handle = self.inner.table(table).map_err(KitError::from)?;
1021 let mut guard = handle.lock();
1022 let res = guard
1023 .aggregate_incremental(cache_key, &conditions, cid, core_agg)
1024 .map_err(KitError::from)?;
1025 Ok(IncrementalAggregate {
1026 value: agg_state_value(&res.state),
1027 incremental: res.incremental,
1028 delta_rows: res.delta_rows,
1029 })
1030 }
1031
1032 pub fn applied_migrations(&self) -> Result<Vec<mongreldb_kit_core::migrations::Migration>> {
1034 crate::migrate::load_applied_migrations(&self.inner)
1035 }
1036
1037 pub(crate) fn core_db(&self) -> &CoreDatabase {
1038 &self.inner
1039 }
1040
1041 pub(crate) fn core_arc(&self) -> Arc<CoreDatabase> {
1044 Arc::clone(&self.inner)
1045 }
1046
1047 pub fn close(&self) -> Result<()> {
1052 self.inner.close().map_err(KitError::from)
1053 }
1054
1055 pub fn compact_all(&self) -> Result<(usize, usize)> {
1060 self.inner.compact().map_err(KitError::from)
1061 }
1062
1063 pub fn compact_table(&self, name: &str) -> Result<bool> {
1066 self.inner.compact_table(name).map_err(KitError::from)
1067 }
1068
1069 pub fn rename_table(&mut self, from: &str, to: &str) -> Result<()> {
1079 if from.starts_with("__kit_") || to.starts_with("__kit_") {
1080 return Err(KitError::Validation(
1081 "rename_table: names beginning with '__kit_' are reserved for internal tables"
1082 .into(),
1083 ));
1084 }
1085 self.inner.rename_table(from, to).map_err(KitError::from)?;
1086 if !self.schema.rename_table(from, to) {
1089 return Err(KitError::Integrity(format!(
1092 "rename_table: kit schema has no table '{from}' (or '{to}' already exists)"
1093 )));
1094 }
1095 for table in &mut self.schema.tables {
1096 for fk in &mut table.foreign_keys {
1097 if fk.references_table == from {
1098 fk.references_table = to.to_string();
1099 }
1100 }
1101 }
1102 store_schema(&self.root, &self.schema)?;
1103 Ok(())
1104 }
1105
1106 pub fn analyze(&self) -> Result<()> {
1111 for name in self.inner.table_names() {
1112 let handle = self.inner.table(&name).map_err(KitError::from)?;
1113 handle.lock().ensure_indexes_complete()?;
1114 }
1115 Ok(())
1116 }
1117
1118 pub fn vacuum(&self) -> Result<usize> {
1122 self.inner.compact().map_err(KitError::from)?;
1123 self.inner.gc().map_err(KitError::from)
1124 }
1125
1126 pub fn create_view(&self, spec: &ViewSpec) -> Result<()> {
1132 self.sql(&spec.create_sql())?;
1133 Ok(())
1134 }
1135
1136 pub fn drop_view(&self, name: &str) -> Result<()> {
1138 self.sql(&format!("DROP VIEW IF EXISTS {name}"))?;
1139 Ok(())
1140 }
1141
1142 pub fn reserve_auto_inc(&self, table: &str) -> Result<Option<i64>> {
1150 let handle = self.inner.table(table).map_err(KitError::from)?;
1151 let mut guard = handle.lock();
1152 guard.reserve_auto_inc().map_err(KitError::from)
1153 }
1154
1155 pub fn create_user(&self, username: &str, password: &str) -> Result<()> {
1159 self.inner
1160 .create_user(username, password)
1161 .map_err(KitError::from)?;
1162 Ok(())
1163 }
1164
1165 pub fn drop_user(&self, username: &str) -> Result<()> {
1167 self.inner.drop_user(username).map_err(KitError::from)
1168 }
1169
1170 pub fn alter_user_password(&self, username: &str, new_password: &str) -> Result<()> {
1172 self.inner
1173 .alter_user_password(username, new_password)
1174 .map_err(KitError::from)
1175 }
1176
1177 pub fn verify_user(
1179 &self,
1180 username: &str,
1181 password: &str,
1182 ) -> Result<Option<mongreldb_core::auth::UserEntry>> {
1183 self.inner
1184 .verify_user(username, password)
1185 .map_err(KitError::from)
1186 }
1187
1188 pub fn set_user_admin(&self, username: &str, is_admin: bool) -> Result<()> {
1190 self.inner
1191 .set_user_admin(username, is_admin)
1192 .map_err(KitError::from)
1193 }
1194
1195 pub fn users(&self) -> Vec<String> {
1197 self.inner.users().into_iter().map(|u| u.username).collect()
1198 }
1199
1200 pub fn create_role(&self, name: &str) -> Result<()> {
1202 self.inner.create_role(name).map_err(KitError::from)?;
1203 Ok(())
1204 }
1205
1206 pub fn drop_role(&self, name: &str) -> Result<()> {
1208 self.inner.drop_role(name).map_err(KitError::from)
1209 }
1210
1211 pub fn roles(&self) -> Vec<String> {
1213 self.inner.roles().into_iter().map(|r| r.name).collect()
1214 }
1215
1216 pub fn grant_role(&self, username: &str, role_name: &str) -> Result<()> {
1218 self.inner
1219 .grant_role(username, role_name)
1220 .map_err(KitError::from)
1221 }
1222
1223 pub fn revoke_role(&self, username: &str, role_name: &str) -> Result<()> {
1225 self.inner
1226 .revoke_role(username, role_name)
1227 .map_err(KitError::from)
1228 }
1229
1230 pub fn grant_permission(
1232 &self,
1233 role_name: &str,
1234 permission: mongreldb_core::auth::Permission,
1235 ) -> Result<()> {
1236 self.inner
1237 .grant_permission(role_name, permission)
1238 .map_err(KitError::from)
1239 }
1240
1241 pub fn revoke_permission(
1243 &self,
1244 role_name: &str,
1245 permission: mongreldb_core::auth::Permission,
1246 ) -> Result<()> {
1247 self.inner
1248 .revoke_permission(role_name, permission)
1249 .map_err(KitError::from)
1250 }
1251
1252 pub fn set_spill_threshold(&self, bytes: u64) {
1258 self.inner.set_spill_threshold(bytes);
1259 }
1260
1261 pub fn set_recursive_triggers(&self, enabled: bool) {
1263 self.inner.set_recursive_triggers(enabled);
1264 }
1265
1266 pub fn trigger_config(&self) -> mongreldb_core::TriggerConfig {
1268 self.inner.trigger_config()
1269 }
1270
1271 pub fn set_trigger_config(&self, config: mongreldb_core::TriggerConfig) -> Result<()> {
1273 self.inner
1274 .set_trigger_config(config)
1275 .map_err(KitError::from)
1276 }
1277
1278 pub fn set_table_compaction_zstd_level(&self, table: &str, level: i32) -> Result<()> {
1280 let handle = self.inner.table(table).map_err(KitError::from)?;
1281 handle.lock().set_compaction_zstd_level(level);
1282 Ok(())
1283 }
1284
1285 pub fn set_table_result_cache_max_bytes(&self, table: &str, max_bytes: u64) -> Result<()> {
1287 let handle = self.inner.table(table).map_err(KitError::from)?;
1288 handle.lock().set_result_cache_max_bytes(max_bytes);
1289 Ok(())
1290 }
1291
1292 pub fn set_table_mutable_run_spill_bytes(&self, table: &str, bytes: u64) -> Result<()> {
1294 let handle = self.inner.table(table).map_err(KitError::from)?;
1295 handle.lock().set_mutable_run_spill_bytes(bytes);
1296 Ok(())
1297 }
1298
1299 pub fn set_table_sync_byte_threshold(&self, table: &str, threshold: u64) -> Result<()> {
1301 let handle = self.inner.table(table).map_err(KitError::from)?;
1302 handle.lock().set_sync_byte_threshold(threshold);
1303 Ok(())
1304 }
1305
1306 pub fn set_table_index_build_policy(
1309 &self,
1310 table: &str,
1311 policy: mongreldb_core::IndexBuildPolicy,
1312 ) -> Result<()> {
1313 let handle = self.inner.table(table).map_err(KitError::from)?;
1314 handle.lock().set_index_build_policy(policy);
1315 Ok(())
1316 }
1317
1318 pub fn table_page_cache_stats(&self, table: &str) -> Result<mongreldb_core::cache::CacheStats> {
1320 let handle = self.inner.table(table).map_err(KitError::from)?;
1321 let stats = handle.lock().page_cache_stats();
1322 Ok(stats)
1323 }
1324
1325 pub fn table_run_count(&self, table: &str) -> Result<usize> {
1327 let handle = self.inner.table(table).map_err(KitError::from)?;
1328 let n = handle.lock().run_count();
1329 Ok(n)
1330 }
1331
1332 pub fn table_memtable_len(&self, table: &str) -> Result<usize> {
1334 let handle = self.inner.table(table).map_err(KitError::from)?;
1335 let n = handle.lock().memtable_len();
1336 Ok(n)
1337 }
1338
1339 pub fn table_mutable_run_len(&self, table: &str) -> Result<usize> {
1341 let handle = self.inner.table(table).map_err(KitError::from)?;
1342 let n = handle.lock().mutable_run_len();
1343 Ok(n)
1344 }
1345
1346 pub fn table_page_cache_len(&self, table: &str) -> Result<usize> {
1348 let handle = self.inner.table(table).map_err(KitError::from)?;
1349 let n = handle.lock().page_cache_len();
1350 Ok(n)
1351 }
1352
1353 pub fn table_decoded_cache_len(&self, table: &str) -> Result<usize> {
1355 let handle = self.inner.table(table).map_err(KitError::from)?;
1356 let n = handle.lock().decoded_cache_len();
1357 Ok(n)
1358 }
1359
1360 pub fn sql(&self, statement: &str) -> Result<Vec<arrow::record_batch::RecordBatch>> {
1378 let session = match self.session.lock().take() {
1384 Some(s) => s,
1385 None => {
1386 mongreldb_query::MongrelSession::open(self.core_arc()).map_err(KitError::from)?
1387 }
1388 };
1389 let runtime = sql_runtime();
1390 let result = runtime
1391 .block_on(session.run(statement))
1392 .map_err(KitError::from);
1393 *self.session.lock() = Some(session);
1395 result
1396 }
1397
1398 pub fn refresh_sql_session(&self) -> Result<()> {
1403 let session =
1404 mongreldb_query::MongrelSession::open(self.core_arc()).map_err(KitError::from)?;
1405 *self.session.lock() = Some(session);
1406 Ok(())
1407 }
1408
1409 pub fn sql_arrow(&self, statement: &str) -> Result<Vec<u8>> {
1415 let batches = self.sql(statement)?;
1416 crate::arrow_util::batches_to_ipc(&batches)
1417 }
1418
1419 pub fn sql_rows(&self, statement: &str) -> Result<Vec<serde_json::Map<String, Value>>> {
1423 let batches = self.sql(statement)?;
1424 crate::arrow_util::batches_to_rows(&batches)
1425 }
1426
1427 pub(crate) fn lookup_row_id(&self, table: &str, key: &[u8]) -> Result<Option<RowId>> {
1431 let handle = self.inner.table(table).map_err(KitError::from)?;
1432 let mut guard = handle.lock();
1433 guard.ensure_indexes_complete()?;
1434 Ok(guard.lookup_pk(key))
1435 }
1436
1437 pub(crate) fn root(&self) -> &Path {
1438 &self.root
1439 }
1440
1441 pub(crate) fn visible_core_rows_at(
1445 &self,
1446 table_name: &str,
1447 snapshot: Snapshot,
1448 ) -> Result<Vec<CoreRow>> {
1449 let handle = self.inner.table(table_name).map_err(KitError::from)?;
1450 let guard = handle.lock();
1451 guard.visible_rows(snapshot).map_err(KitError::from)
1452 }
1453
1454 pub(crate) fn query_core_rows_at(
1461 &self,
1462 table_name: &str,
1463 conditions: &[mongreldb_core::query::Condition],
1464 snapshot: Snapshot,
1465 ) -> Result<Vec<CoreRow>> {
1466 if conditions.is_empty() {
1467 return self.visible_core_rows_at(table_name, snapshot);
1468 }
1469 let handle = self.inner.table(table_name).map_err(KitError::from)?;
1470 let mut guard = handle.lock();
1471 let q = mongreldb_core::query::Query {
1472 conditions: conditions.to_vec(),
1473 };
1474 guard.query(&q).map_err(KitError::from)
1475 }
1476
1477 pub(crate) fn flush_table(&self, table_name: &str) -> Result<()> {
1485 let handle = self.inner.table(table_name).map_err(KitError::from)?;
1486 handle.lock().flush().map_err(KitError::from)?;
1487 Ok(())
1488 }
1489
1490 pub(crate) fn count_core_rows_at(
1500 &self,
1501 table_name: &str,
1502 conditions: &[mongreldb_core::query::Condition],
1503 snapshot: Snapshot,
1504 ) -> Result<Option<u64>> {
1505 let handle = self.inner.table(table_name).map_err(KitError::from)?;
1506 let mut guard = handle.lock();
1507 if guard.snapshot().epoch != snapshot.epoch {
1508 return Ok(None); }
1510 guard
1511 .count_conditions(conditions, snapshot)
1512 .map_err(KitError::from)
1513 }
1514
1515 pub(crate) fn aggregate_core_at(
1524 &self,
1525 table_name: &str,
1526 column: Option<u16>,
1527 conditions: &[mongreldb_core::query::Condition],
1528 agg: NativeAgg,
1529 snapshot: Snapshot,
1530 ) -> Result<Option<NativeAggResult>> {
1531 let handle = self.inner.table(table_name).map_err(KitError::from)?;
1532 let guard = handle.lock();
1533 if guard.snapshot().epoch != snapshot.epoch {
1534 return Ok(None); }
1536 guard
1537 .aggregate_native(snapshot, column, conditions, agg)
1538 .map_err(KitError::from)
1539 }
1540
1541 pub(crate) fn count_distinct_core_at(
1550 &self,
1551 table_name: &str,
1552 column_id: u16,
1553 snapshot: Snapshot,
1554 ) -> Result<Option<u64>> {
1555 let handle = self.inner.table(table_name).map_err(KitError::from)?;
1556 let mut guard = handle.lock();
1557 if guard.snapshot().epoch != snapshot.epoch {
1558 return Ok(None); }
1560 guard
1561 .count_distinct_from_bitmap(column_id)
1562 .map_err(KitError::from)
1563 }
1564
1565 #[allow(dead_code)]
1567 pub(crate) fn get_core_row(&self, table_name: &str, row_id: u64) -> Result<Option<CoreRow>> {
1568 let handle = self.inner.table(table_name).map_err(KitError::from)?;
1569 let guard = handle.lock();
1570 let snapshot = guard.snapshot();
1571 Ok(guard.get(mongreldb_core::RowId(row_id), snapshot))
1572 }
1573}
1574
1575pub(crate) fn create_core_table(db: &CoreDatabase, name: &str, schema: CoreSchema) -> Result<()> {
1576 if db.table_id(name).is_ok() {
1577 return Ok(());
1578 }
1579 db.create_table(name, schema).map_err(KitError::from)?;
1580 Ok(())
1581}
1582
1583fn sql_runtime() -> &'static tokio::runtime::Runtime {
1588 use std::sync::OnceLock;
1589 static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
1590 RT.get_or_init(|| {
1591 tokio::runtime::Builder::new_current_thread()
1592 .enable_all()
1593 .build()
1594 .expect("failed to build kit SQL tokio runtime")
1595 })
1596}
1597
1598fn core_procedure(spec: &ProcedureSpec) -> Result<mongreldb_core::StoredProcedure> {
1599 let parsed: mongreldb_core::StoredProcedure =
1600 serde_json::from_value(spec.json.clone()).map_err(KitError::from)?;
1601 mongreldb_core::StoredProcedure::new(parsed.name, parsed.mode, parsed.params, parsed.body, 0)
1602 .map_err(KitError::from)
1603}
1604
1605fn core_trigger(spec: &TriggerSpec) -> Result<mongreldb_core::StoredTrigger> {
1606 let parsed: mongreldb_core::StoredTrigger =
1607 serde_json::from_value(spec.json.clone()).map_err(KitError::from)?;
1608 mongreldb_core::StoredTrigger::new(
1609 parsed.name,
1610 mongreldb_core::TriggerDefinition {
1611 target: parsed.target,
1612 timing: parsed.timing,
1613 event: parsed.event,
1614 update_of: parsed.update_of,
1615 target_columns: parsed.target_columns,
1616 when: parsed.when,
1617 program: parsed.program,
1618 },
1619 0,
1620 )
1621 .map_err(KitError::from)
1622}
1623
1624fn json_to_core_value(value: &Value) -> Result<CoreValue> {
1625 match value {
1626 Value::Null => Ok(CoreValue::Null),
1627 Value::Bool(value) => Ok(CoreValue::Bool(*value)),
1628 Value::Number(value) => {
1629 if let Some(value) = value.as_i64() {
1630 Ok(CoreValue::Int64(value))
1631 } else if let Some(value) = value.as_f64() {
1632 Ok(CoreValue::Float64(value))
1633 } else {
1634 Err(KitError::Validation("unsupported JSON number".into()))
1635 }
1636 }
1637 Value::String(value) => Ok(CoreValue::Bytes(value.as_bytes().to_vec())),
1638 Value::Array(_) | Value::Object(_) => Err(KitError::Validation(
1639 "procedure args only support scalar JSON values".into(),
1640 )),
1641 }
1642}
1643
1644pub(crate) fn internal_bytes(row: &CoreRow, col_id: u16) -> Option<String> {
1646 match row.columns.get(&col_id) {
1647 Some(CoreValue::Bytes(b)) => String::from_utf8(b.clone()).ok(),
1648 _ => None,
1649 }
1650}
1651
1652fn reap_rotated_wal_segments(db: &CoreDatabase) {
1667 let _ = db.gc();
1668}
1669
1670pub(crate) fn load_schema(path: &Path) -> Result<KitSchema> {
1671 let file = path.join(SCHEMA_FILE);
1672 let json = std::fs::read_to_string(&file)
1673 .map_err(|e| KitError::Migration(format!("cannot read schema file: {e}")))?;
1674 let schema: KitSchema = serde_json::from_str(&json)?;
1675 Ok(schema)
1676}
1677
1678pub(crate) fn store_schema(path: &Path, schema: &KitSchema) -> Result<()> {
1679 let file = path.join(SCHEMA_FILE);
1680 let json = serde_json::to_string_pretty(schema)?;
1681 std::fs::write(&file, json)?;
1682 Ok(())
1683}
1684
1685pub(crate) fn persist_schema(db: &Database, schema: &KitSchema) -> Result<()> {
1687 store_schema(&db.root, schema)
1688}