1use std::sync::Arc;
2
3use anyhow::{Result, bail, ensure};
4use reblessive::tree::Stk;
5
6use crate::ctx::{Context, MutableContext};
7use crate::dbs::capabilities::ExperimentalTarget;
8use crate::dbs::{Options, Statement};
9use crate::doc::Document;
10use crate::err::Error;
11use crate::expr::data::Data;
12use crate::expr::idiom::{Idiom, IdiomTrie, IdiomTrieContains};
13use crate::expr::kind::Kind;
14use crate::expr::permission::Permission;
15use crate::expr::statements::DefineFieldStatement;
16use crate::expr::statements::define::DefineDefault;
17use crate::expr::{FlowResultExt as _, Part};
18use crate::iam::Action;
19use crate::val::value::CoerceError;
20use crate::val::value::every::ArrayBehaviour;
21use crate::val::{RecordId, Value};
22
23fn clean_none(v: &mut Value) -> bool {
26 match v {
27 Value::None => false,
28 Value::Object(o) => {
29 o.retain(|_, v| clean_none(v));
30 true
31 }
32 Value::Array(x) => {
33 x.iter_mut().for_each(|x| {
34 clean_none(x);
35 });
36 true
37 }
38 _ => true,
39 }
40}
41
42impl Document {
43 pub(super) async fn cleanup_table_fields(
48 &mut self,
49 ctx: &Context,
50 opt: &Options,
51 _stm: &Statement<'_>,
52 ) -> Result<()> {
53 let tb = self.tb(ctx, opt).await?;
55 if tb.schemafull {
57 let mut defined_field_names = IdiomTrie::new();
62
63 for fd in self.fd(ctx, opt).await?.iter() {
65 let is_flex = fd.flex;
66 let is_literal = fd.field_kind.as_ref().is_some_and(Kind::contains_literal);
67 for k in self.current.doc.as_ref().each(&fd.name).into_iter() {
68 defined_field_names.insert(&k, is_flex || is_literal);
69 }
70 }
71
72 for current_doc_field_idiom in
74 self.current.doc.as_ref().every(None, true, ArrayBehaviour::Full).iter()
75 {
76 if current_doc_field_idiom.is_special() {
77 continue;
79 }
80
81 match defined_field_names.contains(current_doc_field_idiom) {
83 IdiomTrieContains::Exact(_) => {
84 continue;
86 }
87 IdiomTrieContains::Ancestor(true) => {
88 continue;
94 }
95 IdiomTrieContains::Ancestor(false) => {
96 if let Some(part) = current_doc_field_idiom.last() {
97 if part.is_index() {
99 continue;
101 }
102 }
103
104 ensure!(
107 !opt.strict,
108 Error::FieldUndefined {
110 table: tb.name.clone(),
111 field: current_doc_field_idiom.to_owned(),
112 }
113 );
114
115 self.current.doc.to_mut().cut(current_doc_field_idiom);
117 }
118
119 IdiomTrieContains::None => {
120 ensure!(
123 !opt.strict,
124 Error::FieldUndefined {
126 table: tb.name.clone(),
127 field: current_doc_field_idiom.to_owned(),
128 }
129 );
130
131 self.current.doc.to_mut().cut(current_doc_field_idiom);
133 }
134 }
135 }
136 }
137
138 clean_none(self.current.doc.to_mut());
141 Ok(())
143 }
144
145 pub(super) async fn process_table_fields(
150 &mut self,
151 stk: &mut Stk,
152 ctx: &Context,
153 opt: &Options,
154 stm: &Statement<'_>,
155 ) -> Result<()> {
156 if opt.import {
158 return Ok(());
159 }
160 let rid = self.id()?;
162 let inp = self.initial.doc.as_ref().changed(self.current.doc.as_ref());
164 let mut skip: Option<&Idiom> = None;
168 for fd in self.fd(ctx, opt).await?.iter() {
170 let skipped = match skip {
172 Some(inner) => fd.name.starts_with(inner),
175 None => false,
176 };
177
178 if !skipped {
181 skip = None;
182 }
183
184 for (k, mut val) in self.current.doc.as_ref().walk(&fd.name).into_iter() {
186 let old = Arc::new(self.initial.doc.as_ref().pick(&k));
188 let inp = Arc::new(inp.pick(&k));
190 if fd.name.is_id() {
192 ensure!(
193 self.is_new() || val == *old,
194 Error::FieldReadonly {
195 field: fd.name.clone(),
196 thing: rid.to_string(),
197 }
198 );
199
200 if !self.is_new() {
201 continue;
202 }
203 }
204 if fd.readonly && !self.is_new() {
215 if val.ne(&*old) {
216 match stm.data() {
218 Some(Data::ContentExpression(_)) if val.is_none() => {
223 self.current
224 .doc
225 .to_mut()
226 .set(stk, ctx, opt, &k, old.as_ref().clone())
227 .await?;
228 continue;
229 }
230 _ => {
235 bail!(Error::FieldReadonly {
236 field: fd.name.clone(),
237 thing: rid.to_string(),
238 });
239 }
240 }
241 }
242 continue;
246 }
247 let mut field = FieldEditContext {
249 context: None,
250 doc: self,
251 rid: rid.clone(),
252 def: fd,
253 stk,
254 ctx,
255 opt,
256 old,
257 user_input: inp,
258 };
259 if !skipped {
270 if field.def.computed.is_some() {
271 val = Value::None;
273 } else {
274 val = field.process_default_clause(val).await?;
276 if field.def.value.is_some() {
278 val = field.process_type_clause(val).await?;
280 val = field.process_value_clause(val).await?;
282 }
283 val = field.process_type_clause(val).await?;
285 val = field.process_assert_clause(val).await?;
287 field.process_reference_clause(&val).await?;
289 }
290 }
291 val = field.process_permissions_clause(val).await?;
293 if !skipped {
295 if val.is_none() && fd.field_kind.as_ref().is_some_and(Kind::can_be_none) {
297 skip = Some(&fd.name);
298 }
299 self.current.doc.to_mut().put(&k, val);
301 }
302 }
303 }
304 Ok(())
306 }
307 pub(super) async fn cleanup_table_references(
312 &mut self,
313 stk: &mut Stk,
314 ctx: &Context,
315 opt: &Options,
316 ) -> Result<()> {
317 if opt.import {
319 return Ok(());
320 }
321 let rid = self.id()?;
323 for fd in self.fd(ctx, opt).await?.iter() {
325 if fd.reference.is_none() {
327 continue;
328 }
329
330 for (_, val) in self.current.doc.as_ref().walk(&fd.name).into_iter() {
332 if val.is_none() || val.is_empty_array() {
334 continue;
335 }
336
337 let mut field = FieldEditContext {
339 context: None,
340 doc: self,
341 rid: rid.clone(),
342 def: fd,
343 stk,
344 ctx,
345 opt,
346 old: val.into(),
347 user_input: Value::None.into(),
348 };
349
350 field.process_reference_clause(&Value::None).await?;
352 }
353 }
354
355 Ok(())
356 }
357}
358
359struct FieldEditContext<'a> {
360 context: Option<MutableContext>,
362 def: &'a DefineFieldStatement,
364 stk: &'a mut Stk,
366 ctx: &'a Context,
368 opt: &'a Options,
370 doc: &'a Document,
372 rid: Arc<RecordId>,
374 old: Arc<Value>,
376 user_input: Arc<Value>,
378}
379
380enum RefAction<'a> {
381 Set(&'a RecordId),
382 Delete(Vec<&'a RecordId>, String),
383 Ignore,
384}
385
386impl FieldEditContext<'_> {
387 async fn process_type_clause(&self, val: Value) -> Result<Value> {
389 if let Some(kind) = &self.def.field_kind {
391 if self.def.name.is_id() {
393 if let Value::RecordId(ref id) = val {
395 if !kind.is_record() {
397 let inner = id.key.clone().into_value();
399
400 inner.coerce_to_kind(kind).map_err(|e| Error::FieldCoerce {
402 thing: self.rid.to_string(),
403 field_name: self.def.name.to_string(),
404 error: Box::new(e),
405 })?;
406 }
407 }
408 else {
410 bail!(Error::FieldCoerce {
412 thing: self.rid.to_string(),
413 field_name: "id".to_string(),
414 error: Box::new(CoerceError::InvalidKind {
415 from: val,
416 into: "record".to_string(),
417 }),
418 });
419 }
420 }
421 else {
423 let val = val.coerce_to_kind(kind).map_err(|e| Error::FieldCoerce {
425 thing: self.rid.to_string(),
426 field_name: self.def.name.to_string(),
427 error: Box::new(e),
428 })?;
429 return Ok(val);
431 }
432 }
433 Ok(val)
435 }
436 async fn process_default_clause(&mut self, val: Value) -> Result<Value> {
438 if !val.is_none() {
440 return Ok(val);
441 }
442 if !self.doc.is_new() && !matches!(self.def.default, DefineDefault::Always(_)) {
444 return Ok(val);
445 }
446 let def = match &self.def.default {
448 DefineDefault::Set(v) | DefineDefault::Always(v) => Some(v),
449 _ => match &self.def.value {
450 Some(v) if v.is_static() => Some(v),
452 _ => None,
453 },
454 };
455 if let Some(expr) = def {
457 let now = Arc::new(val);
459 let doc = Some(&self.doc.current);
461 let ctx = match self.context.take() {
463 Some(mut ctx) => {
464 ctx.add_value("after", now.clone());
465 ctx.add_value("value", now);
466 ctx
467 }
468 None => {
469 let mut ctx = MutableContext::new(self.ctx);
470 ctx.add_value("before", self.old.clone());
471 ctx.add_value("input", self.user_input.clone());
472 ctx.add_value("after", now.clone());
473 ctx.add_value("value", now);
474 ctx
475 }
476 };
477 let ctx = ctx.freeze();
479 let val =
481 self.stk.run(|stk| expr.compute(stk, &ctx, self.opt, doc)).await.catch_return()?;
482 self.context = Some(MutableContext::unfreeze(ctx)?);
484 return Ok(val);
486 }
487 Ok(val)
489 }
490 async fn process_value_clause(&mut self, val: Value) -> Result<Value> {
492 if let Some(expr) = &self.def.value {
494 let now = Arc::new(val);
496 let doc = Some(&self.doc.current);
498 let ctx = match self.context.take() {
500 Some(mut ctx) => {
501 ctx.add_value("after", now.clone());
502 ctx.add_value("value", now);
503 ctx
504 }
505 None => {
506 let mut ctx = MutableContext::new(self.ctx);
507 ctx.add_value("before", self.old.clone());
508 ctx.add_value("input", self.user_input.clone());
509 ctx.add_value("after", now.clone());
510 ctx.add_value("value", now);
511 ctx
512 }
513 };
514 let ctx = ctx.freeze();
516 let val =
518 self.stk.run(|stk| expr.compute(stk, &ctx, self.opt, doc)).await.catch_return()?;
519 self.context = Some(MutableContext::unfreeze(ctx)?);
521 return Ok(val);
523 }
524 Ok(val)
526 }
527 async fn process_assert_clause(&mut self, val: Value) -> Result<Value> {
529 if val.is_none() && self.def.field_kind.as_ref().is_some_and(Kind::can_be_none) {
533 return Ok(val);
534 }
535 if let Some(expr) = &self.def.assert {
537 let now = Arc::new(val.clone());
539 let doc = Some(&self.doc.current);
541 let ctx = match self.context.take() {
543 Some(mut ctx) => {
544 ctx.add_value("after", now.clone());
545 ctx.add_value("value", now.clone());
546 ctx
547 }
548 None => {
549 let mut ctx = MutableContext::new(self.ctx);
550 ctx.add_value("before", self.old.clone());
551 ctx.add_value("input", self.user_input.clone());
552 ctx.add_value("after", now.clone());
553 ctx.add_value("value", now.clone());
554 ctx
555 }
556 };
557 let ctx = ctx.freeze();
559 let res =
561 self.stk.run(|stk| expr.compute(stk, &ctx, self.opt, doc)).await.catch_return()?;
562 self.context = Some(MutableContext::unfreeze(ctx)?);
564 ensure!(
566 res.is_truthy(),
567 Error::FieldValue {
568 thing: self.rid.to_string(),
569 field: self.def.name.clone(),
570 check: expr.to_string(),
571 value: now.to_string(),
572 }
573 );
574 }
575 Ok(val)
577 }
578 async fn process_permissions_clause(&mut self, val: Value) -> Result<Value> {
580 if self.opt.check_perms(Action::Edit)? {
582 let perms = if self.doc.is_new() {
584 &self.def.permissions.create
585 } else {
586 &self.def.permissions.update
587 };
588 let val = match perms {
590 Permission::Full => val,
594 Permission::None => {
598 if val != *self.old {
599 self.old.as_ref().clone()
600 } else {
601 val
602 }
603 }
604 Permission::Specific(expr) => {
609 let now = Arc::new(val.clone());
611 let doc = Some(&self.doc.current);
613 let opt = &self.opt.new_with_perms(false);
615 let ctx = match self.context.take() {
618 Some(mut ctx) => {
619 ctx.add_value("after", now.clone());
620 ctx.add_value("value", now);
621 ctx
622 }
623 None => {
624 let mut ctx = MutableContext::new(self.ctx);
625 ctx.add_value("before", self.old.clone());
626 ctx.add_value("input", self.user_input.clone());
627 ctx.add_value("after", now.clone());
628 ctx.add_value("value", now);
629 ctx
630 }
631 };
632 let ctx = ctx.freeze();
634 let res = self
636 .stk
637 .run(|stk| expr.compute(stk, &ctx, opt, doc))
638 .await
639 .catch_return()?;
640 self.context = Some(MutableContext::unfreeze(ctx)?);
642 if res.is_truthy() || val == *self.old {
648 val
649 } else {
650 self.old.as_ref().clone()
651 }
652 }
653 };
654 return Ok(val);
656 }
657 Ok(val)
659 }
660 async fn process_reference_clause(&mut self, val: &Value) -> Result<()> {
662 if !self.ctx.get_capabilities().allows_experimental(&ExperimentalTarget::RecordReferences) {
663 return Ok(());
664 }
665
666 if self.def.reference.is_some() {
668 let doc = Some(&self.doc.current);
669 let old = self.old.as_ref();
670
671 let action = if val == old {
673 RefAction::Ignore
674 } else if let Value::RecordId(thing) = old {
676 let others = self
678 .doc
679 .current
680 .doc
681 .as_ref()
682 .get(self.stk, self.ctx, self.opt, doc, &self.def.name)
683 .await
684 .catch_return()?;
685 if let Value::Array(arr) = others {
688 if arr.iter().any(|v| v == old) {
689 RefAction::Ignore
690 } else {
691 RefAction::Delete(vec![thing], self.def.name.to_string())
692 }
693 } else {
694 RefAction::Delete(vec![thing], self.def.name.to_string())
696 }
697 } else if let Value::Array(oldarr) = old {
698 let removed = if let Value::Array(newarr) = val {
701 oldarr
702 .iter()
703 .filter_map(|v| {
704 if newarr.contains(v) {
707 None
708 } else if let Value::RecordId(thing) = v {
709 Some(thing)
710 } else {
711 None
712 }
713 })
714 .collect()
715
716 } else {
719 oldarr
720 .iter()
721 .filter_map(|v| {
722 if let Value::RecordId(thing) = v {
723 Some(thing)
724 } else {
725 None
726 }
727 })
728 .collect()
729 };
730
731 RefAction::Delete(removed, self.def.name.clone().push(Part::All).to_string())
732 } else if let Value::RecordId(thing) = val {
734 RefAction::Set(thing)
735 } else {
736 RefAction::Ignore
739 };
740
741 match action {
743 RefAction::Ignore => Ok(()),
745 RefAction::Set(thing) => {
747 let (ns, db) = self.ctx.expect_ns_db_ids(self.opt).await?;
748 let name = self.def.name.to_string();
749 let key = crate::key::r#ref::new(
750 ns,
751 db,
752 &thing.table,
753 &thing.key,
754 &self.rid.table,
755 &name,
756 &self.rid.key,
757 );
758
759 self.ctx.tx().set(&key, &(), None).await?;
760
761 Ok(())
762 }
763 RefAction::Delete(things, ff) => {
765 let (ns, db) = self.ctx.expect_ns_db_ids(self.opt).await?;
766 for thing in things {
767 let key = crate::key::r#ref::new(
768 ns,
769 db,
770 &thing.table,
771 &thing.key,
772 &self.rid.table,
773 &ff,
774 &self.rid.key,
775 );
776
777 self.ctx.tx().del(&key).await?;
778 }
779
780 Ok(())
781 }
782 }
783 } else {
784 Ok(())
785 }
786 }
787}