surrealdb_core/doc/
field.rs

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
23/// Removes `NONE` values recursively from objects, but not when `NONE` is a
24/// direct child of an array
25fn 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	/// Ensures that any remaining fields on a
44	/// SCHEMAFULL table are cleaned up and removed.
45	/// If a field is defined as FLEX, then any
46	/// nested fields or array values are untouched.
47	pub(super) async fn cleanup_table_fields(
48		&mut self,
49		ctx: &Context,
50		opt: &Options,
51		_stm: &Statement<'_>,
52	) -> Result<()> {
53		// Get the table
54		let tb = self.tb(ctx, opt).await?;
55		// This table is schemafull
56		if tb.schemafull {
57			// Prune unspecified fields from the document that are not defined via
58			// `DefineFieldStatement`s.
59
60			// Create a vector to store the keys
61			let mut defined_field_names = IdiomTrie::new();
62
63			// Loop through all field definitions
64			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			// Loop over every field in the document
73			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					// This field is a built-in field, so we can skip it.
78					continue;
79				}
80
81				// Check if the field is defined in the schema
82				match defined_field_names.contains(current_doc_field_idiom) {
83					IdiomTrieContains::Exact(_) => {
84						// This field is defined in the schema, so we can skip it.
85						continue;
86					}
87					IdiomTrieContains::Ancestor(true) => {
88						// This field is not explicitly defined in the schema, but it is a child of
89						// a flex or literal field. If the field is a child of a flex field,
90						// then any nested fields are allowed. If the field is a child of a
91						// literal field, then allow any fields as they will be caught during
92						// coercion.
93						continue;
94					}
95					IdiomTrieContains::Ancestor(false) => {
96						if let Some(part) = current_doc_field_idiom.last() {
97							// This field is an array index, so it is automatically allowed.
98							if part.is_index() {
99								// This field is an array index, so we can skip it.
100								continue;
101							}
102						}
103
104						// This field is not explicitly defined in the schema or it is not a child
105						// of a flex field.
106						ensure!(
107							!opt.strict,
108							// If strict, then throw an error on an undefined field
109							Error::FieldUndefined {
110								table: tb.name.clone(),
111								field: current_doc_field_idiom.to_owned(),
112							}
113						);
114
115						// Otherwise, delete the field silently and don't error
116						self.current.doc.to_mut().cut(current_doc_field_idiom);
117					}
118
119					IdiomTrieContains::None => {
120						// This field is not explicitly defined in the schema or it is not a child
121						// of a flex field.
122						ensure!(
123							!opt.strict,
124							// If strict, then throw an error on an undefined field
125							Error::FieldUndefined {
126								table: tb.name.clone(),
127								field: current_doc_field_idiom.to_owned(),
128							}
129						);
130
131						// Otherwise, delete the field silently and don't error
132						self.current.doc.to_mut().cut(current_doc_field_idiom);
133					}
134				}
135			}
136		}
137
138		// Loop over every field in the document
139		// NONE values should never be stored
140		clean_none(self.current.doc.to_mut());
141		// Carry on
142		Ok(())
143	}
144
145	/// Processes `DEFINE FIELD` statements which
146	/// have been defined on the table for this
147	/// record. These fields are executed for
148	/// every matching field in the input document.
149	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		// Check import
157		if opt.import {
158			return Ok(());
159		}
160		// Get the record id
161		let rid = self.id()?;
162		// Get the user applied input
163		let inp = self.initial.doc.as_ref().changed(self.current.doc.as_ref());
164		// When set, any matching embedded object fields
165		// which are prefixed with the specified idiom
166		// will be skipped, as the parent object is optional
167		let mut skip: Option<&Idiom> = None;
168		// Loop through all field statements
169		for fd in self.fd(ctx, opt).await?.iter() {
170			// Check if we should skip this field
171			let skipped = match skip {
172				// We are skipping a parent field
173				// Check if this field is a child field
174				Some(inner) => fd.name.starts_with(inner),
175				None => false,
176			};
177
178			// Let's stop skipping fields if not
179			// Specify whether we should skip
180			if !skipped {
181				skip = None;
182			}
183
184			// Loop over each field in document
185			for (k, mut val) in self.current.doc.as_ref().walk(&fd.name).into_iter() {
186				// Get the initial value
187				let old = Arc::new(self.initial.doc.as_ref().pick(&k));
188				// Get the input value
189				let inp = Arc::new(inp.pick(&k));
190				// Check for the `id` field
191				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 the field is READONLY then we
205				// will check that the field has not
206				// been modified. If it has just been
207				// omitted then we reset it, otherwise
208				// we throw a field readonly error.
209				//
210				// Check if we are updating the
211				// document, and check if the new
212				// field value is now different to
213				// the old field value in any way.
214				if fd.readonly && !self.is_new() {
215					if val.ne(&*old) {
216						// Check the data clause type
217						match stm.data() {
218							// If the field is NONE, we assume
219							// that the field was ommitted when
220							// using a CONTENT clause, and we
221							// revert the value to the old value.
222							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							// If the field has been modified
231							// and the user didn't use a CONTENT
232							// clause, then this should not be
233							// allowed, and we throw an error.
234							_ => {
235								bail!(Error::FieldReadonly {
236									field: fd.name.clone(),
237									thing: rid.to_string(),
238								});
239							}
240						}
241					}
242					// If this field was not modified then
243					// we can continue without needing to
244					// process the field in any other way.
245					continue;
246				}
247				// Generate the field context
248				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				/*
260				// Process a potential `references` TYPE
261				if let Some(v) = field.process_refs_type().await? {
262					todo!()
263					// We found a `references` TYPE
264					// No other clauses will be present, so no need to process them
265					val = v;
266				} else {
267				*/
268				// Skip this field?
269				if !skipped {
270					if field.def.computed.is_some() {
271						// The value will be computed by the `COMPUTED` clause, so we set it to NONE
272						val = Value::None;
273					} else {
274						// Process any DEFAULT clause
275						val = field.process_default_clause(val).await?;
276						// Check for the existance of a VALUE clause
277						if field.def.value.is_some() {
278							// Process any TYPE clause
279							val = field.process_type_clause(val).await?;
280							// Process any VALUE clause
281							val = field.process_value_clause(val).await?;
282						}
283						// Process any TYPE clause
284						val = field.process_type_clause(val).await?;
285						// Process any ASSERT clause
286						val = field.process_assert_clause(val).await?;
287						// Process any REFERENCE clause
288						field.process_reference_clause(&val).await?;
289					}
290				}
291				// Process any PERMISSIONS clause
292				val = field.process_permissions_clause(val).await?;
293				// Skip this field?
294				if !skipped {
295					// If the field is empty, mark child fields as skippable
296					if val.is_none() && fd.field_kind.as_ref().is_some_and(Kind::can_be_none) {
297						skip = Some(&fd.name);
298					}
299					// Set the new value of the field, or delete it if empty
300					self.current.doc.to_mut().put(&k, val);
301				}
302			}
303		}
304		// Carry on
305		Ok(())
306	}
307	/// Processes `DEFINE FIELD` statements which
308	/// have been defined on the table for this
309	/// record, with a `REFERENCE` clause, and remove
310	/// all possible references this record has made.
311	pub(super) async fn cleanup_table_references(
312		&mut self,
313		stk: &mut Stk,
314		ctx: &Context,
315		opt: &Options,
316	) -> Result<()> {
317		// Check import
318		if opt.import {
319			return Ok(());
320		}
321		// Get the record id
322		let rid = self.id()?;
323		// Loop through all field statements
324		for fd in self.fd(ctx, opt).await?.iter() {
325			// Only process reference fields
326			if fd.reference.is_none() {
327				continue;
328			}
329
330			// Loop over each value in document
331			for (_, val) in self.current.doc.as_ref().walk(&fd.name).into_iter() {
332				// Skip if the value is empty
333				if val.is_none() || val.is_empty_array() {
334					continue;
335				}
336
337				// Prepare the field edit context
338				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				// Pass an empty value to delete all the existing references
351				field.process_reference_clause(&Value::None).await?;
352			}
353		}
354
355		Ok(())
356	}
357}
358
359struct FieldEditContext<'a> {
360	/// The mutable request context
361	context: Option<MutableContext>,
362	/// The defined field statement
363	def: &'a DefineFieldStatement,
364	/// The current request stack
365	stk: &'a mut Stk,
366	/// The current request context
367	ctx: &'a Context,
368	/// The current request options
369	opt: &'a Options,
370	/// The current document record being processed
371	doc: &'a Document,
372	/// The record id of the document that we are processing
373	rid: Arc<RecordId>,
374	/// The initial value of the field before being modified
375	old: Arc<Value>,
376	/// The user input value of the field edited by the user
377	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	/// Process any TYPE clause for the field definition
388	async fn process_type_clause(&self, val: Value) -> Result<Value> {
389		// Check for a TYPE clause
390		if let Some(kind) = &self.def.field_kind {
391			// Check if this is the `id` field
392			if self.def.name.is_id() {
393				// Ensure that the outer value is a record
394				if let Value::RecordId(ref id) = val {
395					// See if we should check the inner type
396					if !kind.is_record() {
397						// Get the value of the ID only
398						let inner = id.key.clone().into_value();
399
400						// Check the type of the ID part
401						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				// The outer value should be a record
409				else {
410					// There was a field check error
411					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			// This is not the `id` field
422			else {
423				// Check the type of the field value
424				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 the modified value
430				return Ok(val);
431			}
432		}
433		// Return the original value
434		Ok(val)
435	}
436	/// Process any DEFAULT clause for the field definition
437	async fn process_default_clause(&mut self, val: Value) -> Result<Value> {
438		// This field has a value specified
439		if !val.is_none() {
440			return Ok(val);
441		}
442		// The document is not being created
443		if !self.doc.is_new() && !matches!(self.def.default, DefineDefault::Always(_)) {
444			return Ok(val);
445		}
446		// Get the default value
447		let def = match &self.def.default {
448			DefineDefault::Set(v) | DefineDefault::Always(v) => Some(v),
449			_ => match &self.def.value {
450				// The VALUE clause doesn't
451				Some(v) if v.is_static() => Some(v),
452				_ => None,
453			},
454		};
455		// Check for a DEFAULT clause
456		if let Some(expr) = def {
457			// Arc the current value
458			let now = Arc::new(val);
459			// Get the current document
460			let doc = Some(&self.doc.current);
461			// Configure the context
462			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			// Freeze the new context
478			let ctx = ctx.freeze();
479			// Process the VALUE clause
480			let val =
481				self.stk.run(|stk| expr.compute(stk, &ctx, self.opt, doc)).await.catch_return()?;
482			// Unfreeze the new context
483			self.context = Some(MutableContext::unfreeze(ctx)?);
484			// Return the modified value
485			return Ok(val);
486		}
487		// Return the original value
488		Ok(val)
489	}
490	/// Process any VALUE clause for the field definition
491	async fn process_value_clause(&mut self, val: Value) -> Result<Value> {
492		// Check for a VALUE clause
493		if let Some(expr) = &self.def.value {
494			// Arc the current value
495			let now = Arc::new(val);
496			// Get the current document
497			let doc = Some(&self.doc.current);
498			// Configure the context
499			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			// Freeze the new context
515			let ctx = ctx.freeze();
516			// Process the VALUE clause
517			let val =
518				self.stk.run(|stk| expr.compute(stk, &ctx, self.opt, doc)).await.catch_return()?;
519			// Unfreeze the new context
520			self.context = Some(MutableContext::unfreeze(ctx)?);
521			// Return the modified value
522			return Ok(val);
523		}
524		// Return the original value
525		Ok(val)
526	}
527	/// Process any ASSERT clause for the field definition
528	async fn process_assert_clause(&mut self, val: Value) -> Result<Value> {
529		// If the field TYPE is optional, and the
530		// field value was not set or is NONE we
531		// ignore any defined ASSERT clause.
532		if val.is_none() && self.def.field_kind.as_ref().is_some_and(Kind::can_be_none) {
533			return Ok(val);
534		}
535		// Check for a ASSERT clause
536		if let Some(expr) = &self.def.assert {
537			// Arc the current value
538			let now = Arc::new(val.clone());
539			// Get the current document
540			let doc = Some(&self.doc.current);
541			// Configure the context
542			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			// Freeze the new context
558			let ctx = ctx.freeze();
559			// Process the ASSERT clause
560			let res =
561				self.stk.run(|stk| expr.compute(stk, &ctx, self.opt, doc)).await.catch_return()?;
562			// Unfreeze the new context
563			self.context = Some(MutableContext::unfreeze(ctx)?);
564			// Check the ASSERT clause result
565			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		// Return the original value
576		Ok(val)
577	}
578	/// Process any PERMISSIONS clause for the field definition
579	async fn process_permissions_clause(&mut self, val: Value) -> Result<Value> {
580		// Check for a PERMISSIONS clause
581		if self.opt.check_perms(Action::Edit)? {
582			// Get the permission clause
583			let perms = if self.doc.is_new() {
584				&self.def.permissions.create
585			} else {
586				&self.def.permissions.update
587			};
588			// Match the permission clause
589			let val = match perms {
590				// The field PERMISSIONS clause
591				// is FULL, enabling this field
592				// to be updated without checks.
593				Permission::Full => val,
594				// The field PERMISSIONS clause
595				// is NONE, meaning that this
596				// change will be reverted.
597				Permission::None => {
598					if val != *self.old {
599						self.old.as_ref().clone()
600					} else {
601						val
602					}
603				}
604				// The field PERMISSIONS clause
605				// is a custom expression, so
606				// we check the expression and
607				// revert the field if denied.
608				Permission::Specific(expr) => {
609					// Arc the current value
610					let now = Arc::new(val.clone());
611					// Get the current document
612					let doc = Some(&self.doc.current);
613					// Disable permissions
614					let opt = &self.opt.new_with_perms(false);
615					// Configure the context
616					// Configure the context
617					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					// Freeze the new context
633					let ctx = ctx.freeze();
634					// Process the PERMISSION clause
635					let res = self
636						.stk
637						.run(|stk| expr.compute(stk, &ctx, opt, doc))
638						.await
639						.catch_return()?;
640					// Unfreeze the new context
641					self.context = Some(MutableContext::unfreeze(ctx)?);
642					// If the specific permissions
643					// expression was not truthy,
644					// then this field could not be
645					// updated, meanint that this
646					// change will be reverted.
647					if res.is_truthy() || val == *self.old {
648						val
649					} else {
650						self.old.as_ref().clone()
651					}
652				}
653			};
654			// Return the modified value
655			return Ok(val);
656		}
657		// Return the original value
658		Ok(val)
659	}
660	/// Process any REFERENCE clause for the field definition
661	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		// Is there a `REFERENCE` clause?
667		if self.def.reference.is_some() {
668			let doc = Some(&self.doc.current);
669			let old = self.old.as_ref();
670
671			// If the value has not changed, there is no need to update any references
672			let action = if val == old {
673				RefAction::Ignore
674			// Check if the old value was a record id
675			} else if let Value::RecordId(thing) = old {
676				// We need to check if this reference is contained in an array
677				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 the reference is contained in an array, we only delete it from the array
686				// if there is no other reference to the same record id in the array
687				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					// Otherwise we delete the reference
695					RefAction::Delete(vec![thing], self.def.name.to_string())
696				}
697			} else if let Value::Array(oldarr) = old {
698				// If the new value is still an array, we only filter out the record ids that
699				// are not present in the new array
700				let removed = if let Value::Array(newarr) = val {
701					oldarr
702						.iter()
703						.filter_map(|v| {
704							// If the record id is still present in the new array, we do not remove
705							// the reference
706							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				// If the new value is not an array, then all record ids in the
717				// old array are removed
718				} 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			// We found a new reference, let's create the link
733			} else if let Value::RecordId(thing) = val {
734				RefAction::Set(thing)
735			} else {
736				// This value is not a record id, nothing to process
737				// This can be a containing array for record ids, for example
738				RefAction::Ignore
739			};
740
741			// Process the action
742			match action {
743				// Nothing to process
744				RefAction::Ignore => Ok(()),
745				// Create the reference, if it does not exist yet.
746				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				// Delete the reference, if it exists
764				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}