1mod aggregate;
4mod filter_analyzer;
5mod index_reader;
6mod migration;
7
8use std::cmp::Ordering;
9use std::collections::HashSet;
10
11use wasm_dbms_api::prelude::{
12 AggregateFunction, AggregatedRow, ColumnDef, Database, DbmsError, DbmsResult, DeleteBehavior,
13 Filter, ForeignFetcher, ForeignKeyDef, InsertRecord, JoinColumnDef, MigrationError,
14 MigrationOp, MigrationPolicy, OrderDirection, Query, QueryError, TableColumns, TableError,
15 TableRecord, TableSchema, TransactionError, TransactionId, UpdateRecord, Value, ValuesSource,
16};
17use wasm_dbms_memory::RecordAddress;
18use wasm_dbms_memory::prelude::{MemoryAccess, MemoryProvider, NextRecord, TableRegistry};
19
20use self::filter_analyzer::{IndexPlan, analyze_filter};
21use self::index_reader::{IndexReader, IndexSearchResult};
22use crate::context::DbmsContext;
23use crate::database::migration::snapshots;
24use crate::schema::DatabaseSchema;
25use crate::transaction::journal::{Journal, JournaledWriter};
26use crate::transaction::{DatabaseOverlay, TableOverlay, Transaction, TransactionOp};
27
28const DEFAULT_SELECT_CAPACITY: usize = 128;
30
31fn prime_drift_cache<M>(ctx: &DbmsContext<M>, schema: &dyn DatabaseSchema<M>)
32where
33 M: MemoryProvider,
34{
35 let compiled_hash = snapshots::compute_hash(schema.compiled_snapshots_dyn());
36 let drifted = ctx.schema_registry.borrow().schema_hash() != compiled_hash;
37 ctx.set_drift(compiled_hash, drifted);
38}
39
40pub struct WasmDbmsDatabase<'ctx, M>
45where
46 M: MemoryProvider,
47{
48 ctx: &'ctx DbmsContext<M>,
50 schema: Box<dyn DatabaseSchema<M> + 'ctx>,
52 transaction: Option<TransactionId>,
54}
55
56impl<'ctx, M> WasmDbmsDatabase<'ctx, M>
57where
58 M: MemoryProvider,
59{
60 pub fn oneshot(ctx: &'ctx DbmsContext<M>, schema: impl DatabaseSchema<M> + 'ctx) -> Self {
62 let schema = Box::new(schema);
63 prime_drift_cache(ctx, schema.as_ref());
64 Self {
65 ctx,
66 schema,
67 transaction: None,
68 }
69 }
70
71 pub fn from_transaction(
73 ctx: &'ctx DbmsContext<M>,
74 schema: impl DatabaseSchema<M> + 'ctx,
75 transaction_id: TransactionId,
76 ) -> Self {
77 let schema = Box::new(schema);
78 prime_drift_cache(ctx, schema.as_ref());
79 Self {
80 ctx,
81 schema,
82 transaction: Some(transaction_id),
83 }
84 }
85
86 fn with_transaction_mut<F, R>(&self, f: F) -> DbmsResult<R>
88 where
89 F: FnOnce(&mut Transaction) -> DbmsResult<R>,
90 {
91 let txid = self.transaction.as_ref().ok_or(DbmsError::Transaction(
92 TransactionError::NoActiveTransaction,
93 ))?;
94
95 let mut ts = self.ctx.transaction_session.borrow_mut();
96 let tx = ts.get_transaction_mut(txid)?;
97 f(tx)
98 }
99
100 fn with_transaction<F, R>(&self, f: F) -> DbmsResult<R>
102 where
103 F: FnOnce(&Transaction) -> DbmsResult<R>,
104 {
105 let txid = self.transaction.as_ref().ok_or(DbmsError::Transaction(
106 TransactionError::NoActiveTransaction,
107 ))?;
108
109 let ts = self.ctx.transaction_session.borrow();
110 let tx = ts.get_transaction(txid)?;
111 f(tx)
112 }
113
114 fn validated_transaction_filter(
116 &self,
117 table: &'static str,
118 filters: Vec<Filter>,
119 ) -> DbmsResult<Option<Filter>> {
120 for filter in &filters {
121 let rows = self.schema.select(
122 self,
123 table,
124 Query::builder().all().filter(Some(filter.clone())).build(),
125 )?;
126 if rows.len() != 1 {
127 return Err(DbmsError::Query(QueryError::ConstraintViolation(format!(
128 "transaction conflict on table '{table}'"
129 ))));
130 }
131 }
132
133 Ok(filters.into_iter().reduce(Filter::or))
134 }
135
136 fn drift_check(&self) -> DbmsResult<bool> {
142 if self.ctx.is_migrating() {
143 return Ok(false);
144 }
145 let compiled_hash = snapshots::compute_hash(self.schema.compiled_snapshots_dyn());
146 if let Some(cached) = self.ctx.cached_drift_for(compiled_hash) {
147 return Ok(cached);
148 }
149 let drifted = self.ctx.schema_registry.borrow().schema_hash() != compiled_hash;
150 self.ctx.set_drift(compiled_hash, drifted);
151 Ok(drifted)
152 }
153
154 fn ensure_no_drift(&self) -> DbmsResult<()> {
160 if self.drift_check()? {
161 return Err(DbmsError::Migration(MigrationError::SchemaDrift));
162 }
163 Ok(())
164 }
165
166 fn atomic<F, R>(&self, f: F) -> DbmsResult<R>
180 where
181 F: FnOnce(&WasmDbmsDatabase<'ctx, M>) -> DbmsResult<R>,
182 {
183 let nested = self.ctx.journal.borrow().is_some();
184 if !nested {
185 *self.ctx.journal.borrow_mut() = Some(Journal::new());
186 }
187 match f(self) {
188 Ok(res) => {
189 if !nested && let Some(journal) = self.ctx.journal.borrow_mut().take() {
190 journal.commit();
191 }
192 Ok(res)
193 }
194 Err(err) => {
195 if !nested && let Some(journal) = self.ctx.journal.borrow_mut().take() {
196 journal
197 .rollback(&mut self.ctx.mm.borrow_mut())
198 .expect("critical: failed to rollback journal");
199 }
200 Err(err)
201 }
202 }
203 }
204
205 fn has_foreign_key_references<T>(
209 &self,
210 record_values: &[(ColumnDef, Value)],
211 ) -> DbmsResult<bool>
212 where
213 T: TableSchema,
214 {
215 for (table, column, value) in self.referencing_columns::<T>(record_values)? {
216 let filter = Filter::eq(column, value);
217 let query = Query::builder().field(column).filter(Some(filter)).build();
218 let rows = self.schema.select(self, table, query)?;
219 if !rows.is_empty() {
220 return Ok(true);
221 }
222 }
223 Ok(false)
224 }
225
226 fn delete_foreign_keys_cascade<T>(
228 &self,
229 record_values: &[(ColumnDef, Value)],
230 ) -> DbmsResult<u64>
231 where
232 T: TableSchema,
233 {
234 let mut count = 0;
235 for (table, column, value) in self.referencing_columns::<T>(record_values)? {
236 let filter = Filter::eq(column, value);
237 count += self
238 .schema
239 .delete(self, table, DeleteBehavior::Cascade, Some(filter))?;
240 }
241 Ok(count)
242 }
243
244 fn referencing_columns<T>(
249 &self,
250 record_values: &[(ColumnDef, Value)],
251 ) -> DbmsResult<Vec<(&'static str, &'static str, Value)>>
252 where
253 T: TableSchema,
254 {
255 let mut referencing = Vec::new();
256 for (table, columns) in self.schema.referenced_tables(T::table_name()) {
257 for column in columns {
258 let referenced_column = self.referenced_column(table, column)?;
259 let value = record_values
260 .iter()
261 .find(|(col_def, _)| col_def.name == referenced_column)
262 .map(|(_, value)| value.clone())
263 .ok_or_else(|| {
264 DbmsError::Query(QueryError::UnknownColumn(referenced_column.to_string()))
265 })?;
266 referencing.push((table, column, value));
267 }
268 }
269 Ok(referencing)
270 }
271
272 fn referenced_column(&self, table: &str, column: &str) -> DbmsResult<&'static str> {
279 self.schema
280 .table_columns(table)?
281 .iter()
282 .find(|col_def| col_def.name == column)
283 .and_then(|col_def| col_def.foreign_key.as_ref())
284 .map(|fk| fk.foreign_column)
285 .ok_or_else(|| DbmsError::Query(QueryError::UnknownColumn(column.to_string())))
286 }
287
288 fn overlay(&self) -> DbmsResult<DatabaseOverlay> {
290 self.with_transaction(|tx| Ok(tx.overlay().clone()))
291 }
292
293 fn record_matches_filter(
295 &self,
296 record_values: &[(ColumnDef, Value)],
297 filter: &Filter,
298 ) -> DbmsResult<bool> {
299 filter.matches(record_values).map_err(DbmsError::from)
300 }
301
302 fn apply_distinct(&self, results: &mut Vec<TableColumns>, distinct_by: &[String]) {
309 if distinct_by.is_empty() {
310 return;
311 }
312 let mut seen: HashSet<Vec<Value>> = HashSet::new();
313 results.retain(|record| {
314 let key: Vec<Value> = distinct_by
315 .iter()
316 .map(|col| {
317 Self::this_columns(record)
318 .and_then(|cols| {
319 cols.iter()
320 .find(|(cd, _)| cd.name == col.as_str())
321 .map(|(_, v)| v.clone())
322 })
323 .unwrap_or(Value::Null)
324 })
325 .collect();
326 seen.insert(key)
327 });
328 }
329
330 fn apply_column_selection<T>(&self, results: &mut [TableColumns], query: &Query)
332 where
333 T: TableSchema,
334 {
335 if query.all_selected() {
336 return;
337 }
338 let selected_columns = query.columns::<T>();
339 results
340 .iter_mut()
341 .flat_map(|record| record.iter_mut())
342 .filter(|(source, _)| *source == ValuesSource::This)
343 .for_each(|(_, cols)| {
344 cols.retain(|(col_def, _)| selected_columns.contains(&col_def.name.to_string()));
345 });
346 }
347
348 fn batch_load_eager_relations<T>(
350 &self,
351 results: &mut [TableColumns],
352 query: &Query,
353 ) -> DbmsResult<()>
354 where
355 T: TableSchema,
356 {
357 if query.eager_relations.is_empty() {
358 return Ok(());
359 }
360
361 let fetcher = T::foreign_fetcher();
362
363 for relation in &query.eager_relations {
364 let fk_columns = Self::collect_fk_values::<T>(results, relation)?;
365
366 for (local_column, pk_values) in &fk_columns {
367 let batch_map = fetcher.fetch_batch(self, relation, local_column, pk_values)?;
368
369 Self::verify_fk_batch(&batch_map, pk_values, relation)?;
370 Self::attach_foreign_data(results, &batch_map, relation, local_column);
371 }
372 }
373
374 Ok(())
375 }
376
377 fn collect_fk_values<T>(
379 results: &[TableColumns],
380 relation: &str,
381 ) -> DbmsResult<Vec<(&'static str, Vec<Value>)>>
382 where
383 T: TableSchema,
384 {
385 let mut fk_columns: Vec<(&'static str, HashSet<Value>)> = vec![];
386
387 for record_columns in results {
388 let Some(cols) = Self::this_columns(record_columns) else {
389 continue;
390 };
391
392 let mut found_fk = false;
393 for (col_def, value) in cols {
394 let Some(fk) = &col_def.foreign_key else {
395 continue;
396 };
397 if *fk.foreign_table != *relation {
398 continue;
399 }
400
401 found_fk = true;
402 if value.is_null() {
404 continue;
405 }
406 match fk_columns.iter_mut().find(|(lc, _)| *lc == fk.local_column) {
407 Some((_, values)) => {
408 values.insert(value.clone());
409 }
410 None => {
411 let mut set = HashSet::new();
412 set.insert(value.clone());
413 fk_columns.push((fk.local_column, set));
414 }
415 }
416 }
417
418 if !found_fk {
419 return Err(DbmsError::Query(QueryError::InvalidQuery(format!(
420 "Cannot load relation '{relation}' for table '{}': no foreign key found",
421 T::table_name()
422 ))));
423 }
424 }
425
426 Ok(fk_columns
427 .into_iter()
428 .map(|(col, set)| (col, set.into_iter().collect()))
429 .collect())
430 }
431
432 fn verify_fk_batch(
434 batch_map: &std::collections::HashMap<Value, Vec<(ColumnDef, Value)>>,
435 pk_values: &[Value],
436 relation: &str,
437 ) -> DbmsResult<()> {
438 if let Some(missing) = pk_values.iter().find(|v| !batch_map.contains_key(v)) {
439 return Err(DbmsError::Query(QueryError::BrokenForeignKeyReference {
440 table: relation.to_string(),
441 key: missing.clone(),
442 }));
443 }
444 Ok(())
445 }
446
447 fn attach_foreign_data(
449 results: &mut [TableColumns],
450 batch_map: &std::collections::HashMap<Value, Vec<(ColumnDef, Value)>>,
451 relation: &str,
452 local_column: &str,
453 ) {
454 for record_columns in results.iter_mut() {
455 let fk_value = Self::this_columns(record_columns).and_then(|cols| {
456 cols.iter().find_map(|(col_def, value)| {
457 let fk = col_def.foreign_key.as_ref()?;
458 (fk.foreign_table == relation && fk.local_column == local_column)
459 .then(|| value.clone())
460 })
461 });
462
463 let Some(fk_val) = fk_value else { continue };
464 let Some(foreign_values) = batch_map.get(&fk_val) else {
465 continue;
466 };
467
468 record_columns.push((
469 ValuesSource::Foreign {
470 table: relation.to_string(),
471 column: local_column.to_string(),
472 },
473 foreign_values.clone(),
474 ));
475 }
476 }
477
478 fn this_columns(
480 record: &[(ValuesSource, Vec<(ColumnDef, Value)>)],
481 ) -> Option<&Vec<(ColumnDef, Value)>> {
482 record
483 .iter()
484 .find(|(src, _)| *src == ValuesSource::This)
485 .map(|(_, cols)| cols)
486 }
487
488 #[expect(
490 clippy::type_complexity,
491 reason = "complex return type is necessary for returning both PK and full row data"
492 )]
493 fn existing_rows_for_filter<T>(
494 &self,
495 filter: Option<Filter>,
496 ) -> DbmsResult<Vec<(Value, Vec<(ColumnDef, Value)>)>>
497 where
498 T: TableSchema,
499 {
500 let pk = T::primary_key();
501 let query = Query::builder().filter(filter).build();
502 let records = self.select::<T>(query)?;
503 let rows = records
504 .into_iter()
505 .map(|record| {
506 let values = record.to_values();
507 let pk_value = values
508 .iter()
509 .find(|(col_def, _)| col_def.name == pk)
510 .expect("primary key not found")
511 .1
512 .clone();
513 (pk_value, values)
514 })
515 .collect();
516
517 Ok(rows)
518 }
519
520 fn load_table_registry<T>(&self) -> DbmsResult<TableRegistry>
522 where
523 T: TableSchema,
524 {
525 let sr = self.ctx.schema_registry.borrow();
526 let registry_pages = sr
527 .table_registry_page::<T>()
528 .ok_or(DbmsError::Table(TableError::TableNotFound))?;
529
530 let mut mm = self.ctx.mm.borrow_mut();
531 TableRegistry::load(registry_pages, &mut *mm).map_err(DbmsError::from)
532 }
533
534 fn sort_query_results(
536 &self,
537 results: &mut [TableColumns],
538 column: &str,
539 direction: OrderDirection,
540 ) {
541 results.sort_by(|a, b| {
542 fn get_value<'a>(
543 values: &'a [(ValuesSource, Vec<(ColumnDef, Value)>)],
544 column: &str,
545 ) -> Option<&'a Value> {
546 values
547 .iter()
548 .find(|(source, _)| *source == ValuesSource::This)
549 .and_then(|(_, cols)| {
550 cols.iter()
551 .find(|(col_def, _)| col_def.name == column)
552 .map(|(_, value)| value)
553 })
554 }
555
556 let a_value = get_value(a, column);
557 let b_value = get_value(b, column);
558
559 sort_values_with_direction(a_value, b_value, direction)
560 });
561 }
562
563 fn execute_index_plan<MA>(
564 &self,
565 reader: &IndexReader<'_>,
566 plan: &IndexPlan,
567 mm: &mut MA,
568 ) -> DbmsResult<IndexSearchResult>
569 where
570 MA: MemoryAccess,
571 {
572 let columns = [plan.column()];
573 match plan {
574 IndexPlan::Eq { value, .. } => {
575 let key = [value.clone()];
576 reader
577 .search_eq(&columns, &key, mm)
578 .map_err(DbmsError::from)
579 }
580 IndexPlan::Range { start, end, .. } => {
581 let start_key = start.as_ref().map(|value| vec![value.clone()]);
582 let end_key = end.as_ref().map(|value| vec![value.clone()]);
583 reader
584 .search_range(&columns, start_key.as_deref(), end_key.as_deref(), mm)
585 .map_err(DbmsError::from)
586 }
587 IndexPlan::In { values, .. } => {
588 let keys: Vec<Vec<Value>> =
589 values.iter().cloned().map(|value| vec![value]).collect();
590 reader
591 .search_in(&columns, &keys, mm)
592 .map_err(DbmsError::from)
593 }
594 }
595 }
596
597 fn patch_and_filter(
601 &self,
602 changes: Option<&TableOverlay>,
603 values: Vec<(ColumnDef, Value)>,
604 filter: Option<&Filter>,
605 ) -> DbmsResult<Option<Vec<(ColumnDef, Value)>>> {
606 let values = match changes {
607 Some(overlay) => match overlay.patch_row(values) {
608 Some(patched_values) => patched_values,
609 None => return Ok(None),
610 },
611 None => values,
612 };
613
614 if let Some(filter) = filter
615 && !self.record_matches_filter(&values, filter)?
616 {
617 return Ok(None);
618 }
619
620 Ok(Some(values))
621 }
622
623 #[expect(
624 clippy::type_complexity,
625 reason = "complex return type is necessary for returning addresses and overlay PKs"
626 )]
627 fn try_index_select<T>(
628 &self,
629 query: &Query,
630 table_registry: &TableRegistry,
631 table_overlay: &DatabaseOverlay,
632 ) -> DbmsResult<Option<Vec<Vec<(ColumnDef, Value)>>>>
633 where
634 T: TableSchema,
635 {
636 let Some(filter) = &query.filter else {
637 return Ok(None);
638 };
639
640 let Some(analyzed) = analyze_filter(filter, T::indexes()) else {
641 return Ok(None);
642 };
643
644 let mut mm = self.ctx.mm.borrow_mut();
645 let reader = IndexReader::new(
646 table_registry.index_ledger(),
647 table_overlay.index_overlay(T::table_name()),
648 );
649 let search_result = self.execute_index_plan(&reader, &analyzed.plan, &mut *mm)?;
650
651 let mut indexed_rows = Vec::new();
652 let pk_name = T::primary_key();
653 let table_changes = table_overlay.table_overlay(T::table_name());
654
655 for address in &search_result.addresses {
656 let record: T = table_registry
657 .read_at(*address, &mut *mm)
658 .map_err(DbmsError::from)?;
659 let values = record.to_values();
660 let Some(pk) = values
661 .iter()
662 .find(|(column, _)| column.name == pk_name)
663 .map(|(_, value)| value)
664 else {
665 continue;
666 };
667
668 if search_result.removed_pks.contains(pk) || search_result.overlay_pks.contains(pk) {
669 continue;
670 }
671
672 if let Some(values) =
675 self.patch_and_filter(table_changes, values, analyzed.remaining_filter.as_ref())?
676 {
677 indexed_rows.push(values);
678 }
679 }
680
681 if let Some(overlay) = table_changes {
682 let mut pending_overlay_pks = search_result.overlay_pks.clone();
683
684 for row in overlay.iter_inserted() {
685 let Some(pk) = row
686 .iter()
687 .find(|(column, _)| column.name == pk_name)
688 .map(|(_, value)| value)
689 else {
690 continue;
691 };
692
693 if !pending_overlay_pks.remove(pk) {
694 continue;
695 }
696 if let Some(remaining_filter) = &analyzed.remaining_filter
697 && !self.record_matches_filter(&row, remaining_filter)?
698 {
699 continue;
700 }
701
702 indexed_rows.push(row);
703 }
704
705 if !pending_overlay_pks.is_empty() {
706 let pk_reader = IndexReader::new(table_registry.index_ledger(), None);
707 let pk_columns = [T::primary_key()];
708
709 for pk in pending_overlay_pks {
710 let pk_key = [pk];
711 let pk_lookup = pk_reader.search_eq(&pk_columns, &pk_key, &mut *mm)?;
712 for address in pk_lookup.addresses {
713 let record: T = table_registry
714 .read_at(address, &mut *mm)
715 .map_err(DbmsError::from)?;
716 if let Some(values) = self.patch_and_filter(
717 Some(overlay),
718 record.to_values(),
719 analyzed.remaining_filter.as_ref(),
720 )? {
721 indexed_rows.push(values);
722 }
723 }
724 }
725 }
726 }
727
728 Ok(Some(indexed_rows))
729 }
730
731 #[doc(hidden)]
733 pub fn select_columns<T>(&self, query: Query) -> DbmsResult<Vec<TableColumns>>
734 where
735 T: TableSchema,
736 {
737 reject_aggregate_clauses(&query)?;
738 let table_registry = self.load_table_registry::<T>()?;
739 let mut table_overlay = if self.transaction.is_some() {
740 self.overlay()?
741 } else {
742 DatabaseOverlay::default()
743 };
744
745 let initial_capacity = query
746 .limit
747 .unwrap_or(DEFAULT_SELECT_CAPACITY)
748 .min(DEFAULT_SELECT_CAPACITY);
749 let mut results = Vec::with_capacity(initial_capacity);
750 let has_order_by = !query.order_by.is_empty();
754 let has_distinct = !query.distinct_by.is_empty();
755 let defer_pagination = has_order_by || has_distinct;
756 let mut count = 0;
757
758 if let Some(indexed_rows) =
759 self.try_index_select::<T>(&query, &table_registry, &table_overlay)?
760 {
761 for values in indexed_rows {
762 if !defer_pagination {
763 count += 1;
764 if query.offset.is_some_and(|offset| count <= offset) {
765 continue;
766 }
767 }
768 if !defer_pagination && query.limit.is_some_and(|limit| results.len() >= limit) {
769 break;
770 }
771 results.push(vec![(ValuesSource::This, values)]);
772 if !defer_pagination && query.limit.is_some_and(|limit| results.len() >= limit) {
773 break;
774 }
775 }
776 } else {
777 let mut mm = self.ctx.mm.borrow_mut();
778 let table_reader = table_registry.read::<T, _>(&mut *mm);
779 let mut table_reader = table_overlay.reader(table_reader);
780
781 while let Some(values) = table_reader.try_next()? {
782 if let Some(filter) = &query.filter
783 && !self.record_matches_filter(&values, filter)?
784 {
785 continue;
786 }
787 if !defer_pagination {
788 count += 1;
789 if query.offset.is_some_and(|offset| count <= offset) {
790 continue;
791 }
792 }
793 if !defer_pagination && query.limit.is_some_and(|limit| results.len() >= limit) {
794 break;
795 }
796 results.push(vec![(ValuesSource::This, values)]);
797 if !defer_pagination && query.limit.is_some_and(|limit| results.len() >= limit) {
798 break;
799 }
800 }
801 }
802
803 self.apply_distinct(&mut results, &query.distinct_by);
804 self.batch_load_eager_relations::<T>(&mut results, &query)?;
805 self.apply_column_selection::<T>(&mut results, &query);
806
807 for (column, direction) in query.order_by.into_iter().rev() {
808 self.sort_query_results(&mut results, &column, direction);
809 }
810
811 if defer_pagination {
813 let offset = query.offset.unwrap_or_default();
814 if offset > 0 {
815 if offset >= results.len() {
816 results.clear();
817 } else {
818 results.drain(..offset);
819 }
820 }
821 if let Some(limit) = query.limit {
822 results.truncate(limit);
823 }
824 }
825
826 Ok(results)
827 }
828
829 fn select_join_inner(
831 &self,
832 table: &str,
833 query: Query,
834 ) -> DbmsResult<Vec<Vec<(JoinColumnDef, Value)>>> {
835 reject_aggregate_clauses(&query)?;
836 self.schema.select_join(self, table, query)
837 }
838
839 fn update_references_to_updated_record<T>(
845 &self,
846 old_values: &[(ColumnDef, Value)],
847 new_values: &[(ColumnDef, Value)],
848 ) -> DbmsResult<u64>
849 where
850 T: TableSchema,
851 {
852 let mut count = 0;
853 for (ref_table, ref_columns) in self.schema.referenced_tables(T::table_name()) {
854 for ref_col in ref_columns {
855 let referenced_column = self.referenced_column(ref_table, ref_col)?;
856 let find = |values: &[(ColumnDef, Value)]| {
857 values
858 .iter()
859 .find(|(col_def, _)| col_def.name == referenced_column)
860 .map(|(col_def, value)| (*col_def, value.clone()))
861 };
862 let (Some((col_def, old_value)), Some((_, new_value))) =
863 (find(old_values), find(new_values))
864 else {
865 continue;
866 };
867 if old_value == new_value {
868 continue;
869 }
870
871 let ref_patch_value = (
872 ColumnDef {
873 name: ref_col,
874 data_type: col_def.data_type,
875 auto_increment: false,
876 nullable: false,
877 primary_key: false,
878 unique: false,
879 foreign_key: Some(ForeignKeyDef {
880 foreign_table: T::table_name(),
881 foreign_column: referenced_column,
882 local_column: ref_col,
883 }),
884 default: None,
885 renamed_from: &[],
886 },
887 new_value,
888 );
889 let filter = Filter::eq(ref_col, old_value);
890
891 count += self
892 .schema
893 .update(self, ref_table, &[ref_patch_value], Some(filter))?;
894 }
895 }
896
897 Ok(count)
898 }
899
900 fn sanitize_values<T>(
902 &self,
903 values: Vec<(ColumnDef, Value)>,
904 ) -> DbmsResult<Vec<(ColumnDef, Value)>>
905 where
906 T: TableSchema,
907 {
908 let mut sanitized_values = Vec::with_capacity(values.len());
909 for (col_def, value) in values.into_iter() {
910 let value = match T::sanitizer(col_def.name) {
911 Some(sanitizer) => sanitizer.sanitize(value)?,
912 None => value,
913 };
914 sanitized_values.push((col_def, value));
915 }
916 Ok(sanitized_values)
917 }
918
919 #[allow(clippy::type_complexity)]
921 fn collect_matching_records<T>(
922 &self,
923 table_registry: &TableRegistry,
924 filter: &Option<Filter>,
925 ) -> DbmsResult<Vec<(NextRecord<T>, Vec<(ColumnDef, Value)>)>>
926 where
927 T: TableSchema,
928 {
929 let mut mm = self.ctx.mm.borrow_mut();
930
931 if let Some(filter) = filter
936 && let Some(analyzed) = analyze_filter(filter, T::indexes())
937 {
938 let reader = IndexReader::new(table_registry.index_ledger(), None);
939 let search_result = self.execute_index_plan(&reader, &analyzed.plan, &mut *mm)?;
940
941 let mut records = Vec::new();
942 for address in search_result.addresses {
943 let record: T = table_registry
944 .read_at(address, &mut *mm)
945 .map_err(DbmsError::from)?;
946 let record_values = record.clone().to_values();
947 if let Some(remaining_filter) = &analyzed.remaining_filter
948 && !self.record_matches_filter(&record_values, remaining_filter)?
949 {
950 continue;
951 }
952 records.push((
953 NextRecord {
954 record,
955 page: address.page,
956 offset: address.offset,
957 },
958 record_values,
959 ));
960 }
961
962 return Ok(records);
963 }
964
965 let mut table_reader = table_registry.read::<T, _>(&mut *mm);
966 let mut records = vec![];
967 while let Some(values) = table_reader.try_next()? {
968 let record_values = values.record.clone().to_values();
969 if let Some(filter) = filter
970 && !self.record_matches_filter(&record_values, filter)?
971 {
972 continue;
973 }
974 records.push((values, record_values));
975 }
976 Ok(records)
977 }
978
979 fn insert_index<T>(
981 &self,
982 table_registry: &mut TableRegistry,
983 record_address: RecordAddress,
984 values: &[(ColumnDef, Value)],
985 mm: &mut impl wasm_dbms_memory::MemoryAccess,
986 ) -> DbmsResult<()>
987 where
988 T: TableSchema,
989 {
990 let index_ledger = table_registry.index_ledger_mut();
991 for columns in T::indexes().iter().map(|index| index.columns()) {
992 let key = index_key(columns, values);
993 index_ledger.insert(columns, key, record_address, mm)?;
994 }
995
996 Ok(())
997 }
998
999 fn delete_index<T>(
1001 &self,
1002 table_registry: &mut TableRegistry,
1003 record_address: RecordAddress,
1004 values: &[(ColumnDef, Value)],
1005 mm: &mut impl wasm_dbms_memory::MemoryAccess,
1006 ) -> DbmsResult<()>
1007 where
1008 T: TableSchema,
1009 {
1010 let index_ledger = table_registry.index_ledger_mut();
1011 for columns in T::indexes().iter().map(|index| index.columns()) {
1012 let key = index_key(columns, values);
1013 index_ledger.delete(columns, &key, record_address, mm)?;
1014 }
1015 Ok(())
1016 }
1017
1018 fn update_index<T>(
1023 &self,
1024 table_registry: &mut TableRegistry,
1025 old_record_address: RecordAddress,
1026 new_record_address: RecordAddress,
1027 old_values: &[(ColumnDef, Value)],
1028 new_values: &[(ColumnDef, Value)],
1029 mm: &mut impl wasm_dbms_memory::MemoryAccess,
1030 ) -> DbmsResult<()>
1031 where
1032 T: TableSchema,
1033 {
1034 let index_ledger = table_registry.index_ledger_mut();
1035 for columns in T::indexes().iter().map(|index| index.columns()) {
1036 let old_key = index_key(columns, old_values);
1037 let new_key = index_key(columns, new_values);
1038 if old_key == new_key {
1039 index_ledger.update(
1040 columns,
1041 &new_key,
1042 old_record_address,
1043 new_record_address,
1044 mm,
1045 )?;
1046 } else {
1047 index_ledger.delete(columns, &old_key, old_record_address, mm)?;
1048 index_ledger.insert(columns, new_key, new_record_address, mm)?;
1049 }
1050 }
1051 Ok(())
1052 }
1053
1054 fn fill_auto_increment_values<T>(
1056 &self,
1057 table_registry: &mut TableRegistry,
1058 mut values: Vec<(ColumnDef, Value)>,
1059 ) -> DbmsResult<Vec<(ColumnDef, Value)>>
1060 where
1061 T: TableSchema,
1062 {
1063 let mut mm = self.ctx.mm.borrow_mut();
1064 for auto_increment_column in T::columns().iter().filter(|col| col.auto_increment) {
1066 if values
1067 .iter()
1068 .any(|(col_def, _)| col_def.name == auto_increment_column.name)
1069 {
1070 continue;
1071 }
1072 let next_value = table_registry
1073 .next_autoincrement(auto_increment_column.name, &mut *mm)?
1074 .ok_or(DbmsError::Table(TableError::SchemaMismatch))?;
1075 values.push((*auto_increment_column, next_value));
1076 }
1077
1078 Ok(values)
1079 }
1080}
1081
1082fn reject_aggregate_clauses(query: &Query) -> DbmsResult<()> {
1090 if !query.group_by.is_empty() || query.having.is_some() {
1091 return Err(DbmsError::Query(QueryError::AggregateClauseInSelect));
1092 }
1093 Ok(())
1094}
1095
1096pub fn sort_values_with_direction(
1098 a: Option<&Value>,
1099 b: Option<&Value>,
1100 direction: OrderDirection,
1101) -> Ordering {
1102 match (a, b) {
1103 (Some(a_val), Some(b_val)) => match direction {
1104 OrderDirection::Ascending => a_val.cmp(b_val),
1105 OrderDirection::Descending => b_val.cmp(a_val),
1106 },
1107 (Some(_), None) => std::cmp::Ordering::Greater,
1108 (None, Some(_)) => std::cmp::Ordering::Less,
1109 (None, None) => std::cmp::Ordering::Equal,
1110 }
1111}
1112
1113fn values_to_schema_entity<T>(values: Vec<(ColumnDef, Value)>) -> DbmsResult<T>
1115where
1116 T: TableSchema,
1117{
1118 let record = T::Insert::from_values(&values)?.into_record();
1119 Ok(record)
1120}
1121
1122fn index_key(columns: &[&str], values: &[(ColumnDef, Value)]) -> Vec<Value> {
1126 columns
1127 .iter()
1128 .map(|col| {
1129 values
1130 .iter()
1131 .find(|(cd, _)| cd.name == *col)
1132 .map(|(_, v)| v.clone())
1133 .unwrap_or(Value::Null)
1134 })
1135 .collect()
1136}
1137
1138impl<M> Database for WasmDbmsDatabase<'_, M>
1139where
1140 M: MemoryProvider,
1141{
1142 fn select<T>(&self, query: Query) -> DbmsResult<Vec<T::Record>>
1143 where
1144 T: TableSchema,
1145 {
1146 self.ensure_no_drift()?;
1147 if !query.joins.is_empty() {
1148 return Err(DbmsError::Query(QueryError::JoinInsideTypedSelect));
1149 }
1150 let results = self.select_columns::<T>(query)?;
1151 Ok(results.into_iter().map(T::Record::from_values).collect())
1152 }
1153
1154 fn select_raw(&self, table: &str, query: Query) -> DbmsResult<Vec<Vec<(ColumnDef, Value)>>> {
1155 self.ensure_no_drift()?;
1156 self.schema.select(self, table, query)
1157 }
1158
1159 fn select_join(
1160 &self,
1161 table: &str,
1162 query: Query,
1163 ) -> DbmsResult<Vec<Vec<(JoinColumnDef, Value)>>> {
1164 self.ensure_no_drift()?;
1165 self.select_join_inner(table, query)
1166 }
1167
1168 fn aggregate<T>(
1169 &self,
1170 query: Query,
1171 aggregates: &[AggregateFunction],
1172 ) -> DbmsResult<Vec<AggregatedRow>>
1173 where
1174 T: TableSchema,
1175 {
1176 self.ensure_no_drift()?;
1177 aggregate::run_aggregate::<T, _>(self, query, aggregates)
1178 }
1179
1180 fn insert<T>(&self, record: T::Insert) -> DbmsResult<()>
1181 where
1182 T: TableSchema,
1183 T::Insert: InsertRecord<Schema = T>,
1184 {
1185 self.ensure_no_drift()?;
1186 let mut table_registry = self.load_table_registry::<T>()?;
1187 let record_values = record.clone().into_values();
1188 let record_values =
1189 self.fill_auto_increment_values::<T>(&mut table_registry, record_values)?;
1190 let sanitized_values = self.sanitize_values::<T>(record_values)?;
1191 self.schema
1192 .validate_insert(self, T::table_name(), &sanitized_values)?;
1193 if self.transaction.is_some() {
1194 self.with_transaction_mut(|tx| tx.insert::<T>(sanitized_values))?;
1195 } else {
1196 self.atomic(|db| {
1197 let record = T::Insert::from_values(&sanitized_values)?;
1198 let mut mm = db.ctx.mm.borrow_mut();
1199 let mut journal_ref = db.ctx.journal.borrow_mut();
1201 let journal = journal_ref
1202 .as_mut()
1203 .expect("journal must be active inside atomic");
1204 let mut writer = JournaledWriter::new(&mut *mm, journal);
1205 let record_address = table_registry
1207 .insert(record.into_record(), &mut writer)
1208 .map_err(DbmsError::from)?;
1209 self.insert_index::<T>(
1210 &mut table_registry,
1211 record_address,
1212 &sanitized_values,
1213 &mut writer,
1214 )?;
1215 Ok(())
1216 })?;
1217 }
1218
1219 Ok(())
1220 }
1221
1222 fn update<T>(&self, patch: T::Update) -> DbmsResult<u64>
1223 where
1224 T: TableSchema,
1225 T::Update: UpdateRecord<Schema = T>,
1226 {
1227 self.ensure_no_drift()?;
1228 let filter = patch.where_clause().clone();
1229 if self.transaction.is_some() {
1230 let rows = self.existing_rows_for_filter::<T>(filter.clone())?;
1231 let count = rows.len() as u64;
1232 self.with_transaction_mut(|tx| tx.update::<T>(patch, rows))?;
1233
1234 return Ok(count);
1235 }
1236
1237 let patch = patch.update_values();
1238
1239 self.atomic(|db| {
1240 let mut count = 0;
1241
1242 let mut table_registry = db.load_table_registry::<T>()?;
1243 let records = db.collect_matching_records::<T>(&table_registry, &filter)?;
1244
1245 for (record, record_values) in records {
1246 let current_pk_value = record_values
1247 .iter()
1248 .find(|(col_def, _)| col_def.primary_key)
1249 .expect("primary key not found")
1250 .1
1251 .clone();
1252
1253 let previous_record = values_to_schema_entity::<T>(record_values.clone())?;
1254 let old_values_for_index = record_values.clone();
1255 let mut record_values = record_values;
1256
1257 for (patch_col_def, patch_value) in &patch {
1258 if let Some((_, record_value)) = record_values
1259 .iter_mut()
1260 .find(|(record_col_def, _)| record_col_def.name == patch_col_def.name)
1261 {
1262 *record_value = patch_value.clone();
1263 }
1264 }
1265 let record_values = db.sanitize_values::<T>(record_values)?;
1266 db.schema.validate_update(
1267 db,
1268 T::table_name(),
1269 &record_values,
1270 current_pk_value.clone(),
1271 )?;
1272 let updated_record = values_to_schema_entity::<T>(record_values.clone())?;
1273 {
1274 let mut mm = db.ctx.mm.borrow_mut();
1275 let mut journal_ref = db.ctx.journal.borrow_mut();
1277 let journal = journal_ref
1278 .as_mut()
1279 .expect("journal must be active inside atomic");
1280 let mut writer = JournaledWriter::new(&mut *mm, journal);
1281 let old_address = RecordAddress::new(record.page, record.offset);
1283 let new_address = table_registry
1284 .update(updated_record, previous_record, old_address, &mut writer)
1285 .map_err(DbmsError::from)?;
1286 self.update_index::<T>(
1288 &mut table_registry,
1289 old_address,
1290 new_address,
1291 &old_values_for_index,
1292 &record_values,
1293 &mut writer,
1294 )?;
1295 }
1296 count += 1;
1297
1298 count += db.update_references_to_updated_record::<T>(
1299 &old_values_for_index,
1300 &record_values,
1301 )?;
1302 }
1303
1304 Ok(count)
1305 })
1306 }
1307
1308 fn delete<T>(&self, behaviour: DeleteBehavior, filter: Option<Filter>) -> DbmsResult<u64>
1309 where
1310 T: TableSchema,
1311 {
1312 self.ensure_no_drift()?;
1313 if self.transaction.is_some() {
1314 let rows = self.existing_rows_for_filter::<T>(filter.clone())?;
1315 let count = rows.len() as u64;
1316
1317 self.with_transaction_mut(|tx| tx.delete::<T>(behaviour, rows))?;
1318
1319 return Ok(count);
1320 }
1321
1322 self.atomic(|db| {
1323 let mut table_registry = db.load_table_registry::<T>()?;
1324 let records = db.collect_matching_records::<T>(&table_registry, &filter)?;
1325 let mut count = records.len() as u64;
1326 for (record, record_values) in records {
1327 match behaviour {
1328 DeleteBehavior::Cascade => {
1329 count += db.delete_foreign_keys_cascade::<T>(&record_values)?;
1330 }
1331 DeleteBehavior::Restrict => {
1332 if db.has_foreign_key_references::<T>(&record_values)? {
1333 return Err(DbmsError::Query(
1334 QueryError::ForeignKeyConstraintViolation {
1335 referencing_table: T::table_name().to_string(),
1336 field: T::primary_key().to_string(),
1337 },
1338 ));
1339 }
1340 }
1341 }
1342 let mut mm = db.ctx.mm.borrow_mut();
1343 let mut journal_ref = db.ctx.journal.borrow_mut();
1344 let journal = journal_ref
1345 .as_mut()
1346 .expect("journal must be active inside atomic");
1347 let mut writer = JournaledWriter::new(&mut *mm, journal);
1349 let address = RecordAddress::new(record.page, record.offset);
1350 table_registry
1351 .delete(record.record, address, &mut writer)
1352 .map_err(DbmsError::from)?;
1353 self.delete_index::<T>(&mut table_registry, address, &record_values, &mut writer)?;
1354 }
1355
1356 Ok(count)
1357 })
1358 }
1359
1360 fn commit(&mut self) -> DbmsResult<()> {
1361 self.ensure_no_drift()?;
1362 let Some(txid) = self.transaction.take() else {
1363 return Err(DbmsError::Transaction(
1364 TransactionError::NoActiveTransaction,
1365 ));
1366 };
1367 let transaction = {
1368 let mut ts = self.ctx.transaction_session.borrow_mut();
1369 ts.take_transaction(&txid)?
1370 };
1371
1372 *self.ctx.journal.borrow_mut() = Some(Journal::new());
1373
1374 for op in transaction.operations {
1375 let result = match op {
1376 TransactionOp::Insert { table, values } => self
1377 .schema
1378 .validate_insert(self, table, &values)
1379 .and_then(|()| self.schema.insert(self, table, &values)),
1380 TransactionOp::Delete {
1381 table,
1382 behaviour,
1383 filters,
1384 } => self
1385 .validated_transaction_filter(table, filters)
1386 .and_then(|filter| match filter {
1387 Some(filter) => self
1388 .schema
1389 .delete(self, table, behaviour, Some(filter))
1390 .map(|_| ()),
1391 None => Ok(()),
1392 }),
1393 TransactionOp::Update {
1394 table,
1395 patch,
1396 filters,
1397 } => self
1398 .validated_transaction_filter(table, filters)
1399 .and_then(|filter| match filter {
1400 Some(filter) => self
1401 .schema
1402 .update(self, table, &patch, Some(filter))
1403 .map(|_| ()),
1404 None => Ok(()),
1405 }),
1406 };
1407
1408 if let Err(err) = result {
1409 if let Some(journal) = self.ctx.journal.borrow_mut().take() {
1410 journal
1411 .rollback(&mut self.ctx.mm.borrow_mut())
1412 .expect("critical: failed to rollback journal");
1413 }
1414 return Err(err);
1415 }
1416 }
1417
1418 if let Some(journal) = self.ctx.journal.borrow_mut().take() {
1419 journal.commit();
1420 }
1421 Ok(())
1422 }
1423
1424 fn rollback(&mut self) -> DbmsResult<()> {
1425 let Some(txid) = self.transaction.take() else {
1426 return Err(DbmsError::Transaction(
1427 TransactionError::NoActiveTransaction,
1428 ));
1429 };
1430
1431 let mut ts = self.ctx.transaction_session.borrow_mut();
1432 ts.close_transaction(&txid);
1433 Ok(())
1434 }
1435
1436 fn has_drift(&self) -> DbmsResult<bool> {
1437 self.drift_check()
1438 }
1439
1440 fn pending_migrations(&self) -> DbmsResult<Vec<MigrationOp>> {
1441 let stored = {
1442 let sr = self.ctx.schema_registry.borrow();
1443 let mut mm = self.ctx.mm.borrow_mut();
1444 sr.stored_snapshots(&mut *mm)?
1445 };
1446 let compiled = self.schema.compiled_snapshots_dyn();
1447 migration::diff::diff(&stored, &compiled, self.schema.as_ref())
1448 }
1449
1450 fn migrate(&mut self, policy: MigrationPolicy) -> DbmsResult<()> {
1451 let mut ops = self.pending_migrations()?;
1452 migration::plan::validate(&ops, policy)?;
1453 migration::plan::order_ops(&mut ops);
1454 migration::apply::apply(self, ops)
1455 }
1456}
1457
1458#[cfg(test)]
1459mod tests;