Skip to main content

surrealdb_expr/expr/
literal.rs

1use std::hash::{Hash, Hasher};
2
3use rust_decimal::Decimal;
4use surrealdb_strand::Strand;
5use surrealdb_types::{SqlFormat, ToSql};
6
7use crate::expr::{Expr, RecordIdLit};
8use crate::val::{Bytes, Datetime, Duration, File, Geometry, Regex, Uuid};
9
10// Note: Literal uses `Strand` (not `String`) for the inner value of
11// `Literal::String` so that converting between `Value` and `Expr` via
12// `into_literal()` is a zero-cost move rather than a heap allocation.
13// `Literal` is not revisioned, so there is no on-disk compatibility
14// concern with changing the inner type.
15
16/// A literal value, should be computed to get an actual value.
17///
18/// # Note regarding equality.
19/// A literal is equal to an other literal if it is the exact same byte
20/// representation, so normal float rules regarding equality do not apply, i.e.
21/// if `a != b` then `Literal::Float(a)` could still be equal
22/// to `Literal::Float(b)` in the case of `NaN` floats for example. Also
23/// surrealql rules regarding number equality are not observed, 1f != 1dec.
24
25#[derive(Clone, Debug)]
26pub enum Literal {
27	None,
28	Null,
29	// An unbounded range, i.e. `..` without any start or end bound.
30	UnboundedRange,
31	Bool(bool),
32	Float(f64),
33	Integer(i64),
34	Decimal(Decimal),
35	String(Strand),
36	Bytes(Bytes),
37	Regex(Regex),
38	RecordId(RecordIdLit),
39	Array(Vec<Expr>),
40	Set(Vec<Expr>),
41	Object(Vec<ObjectEntry>),
42	Duration(Duration),
43	Datetime(Datetime),
44	Uuid(Uuid),
45	Geometry(Geometry),
46	File(File),
47}
48
49impl Literal {
50	pub fn is_static(&self) -> bool {
51		match self {
52			Literal::None
53			| Literal::Null
54			| Literal::UnboundedRange
55			| Literal::Bool(_)
56			| Literal::Float(_)
57			| Literal::Integer(_)
58			| Literal::Decimal(_)
59			| Literal::String(_)
60			| Literal::Bytes(_)
61			| Literal::Regex(_)
62			| Literal::Duration(_)
63			| Literal::Datetime(_)
64			| Literal::Uuid(_)
65			| Literal::File(_)
66			| Literal::Geometry(_) => true,
67			Literal::RecordId(record_id_lit) => record_id_lit.is_static(),
68			Literal::Array(exprs) => exprs.iter().all(|x| x.is_static()),
69			Literal::Set(exprs) => exprs.iter().all(|x| x.is_static()),
70			Literal::Object(items) => items.iter().all(|x| x.value.is_static()),
71		}
72	}
73
74	/// Whether evaluating this literal can modify data. Composite literals
75	/// (arrays, sets, objects, record ids) evaluate their element expressions
76	/// in place, so those must be inspected; scalars cannot write.
77	pub fn read_only(&self) -> bool {
78		match self {
79			Literal::None
80			| Literal::Null
81			| Literal::UnboundedRange
82			| Literal::Bool(_)
83			| Literal::Float(_)
84			| Literal::Integer(_)
85			| Literal::Decimal(_)
86			| Literal::String(_)
87			| Literal::Bytes(_)
88			| Literal::Regex(_)
89			| Literal::Duration(_)
90			| Literal::Datetime(_)
91			| Literal::Uuid(_)
92			| Literal::File(_)
93			| Literal::Geometry(_) => true,
94			Literal::RecordId(record_id_lit) => record_id_lit.read_only(),
95			Literal::Array(exprs) => exprs.iter().all(|x| x.read_only()),
96			Literal::Set(exprs) => exprs.iter().all(|x| x.read_only()),
97			Literal::Object(items) => items.iter().all(|x| x.value.read_only()),
98		}
99	}
100
101	/// The value this literal denotes, when it denotes one on its own.
102	///
103	/// `Some` when no part of the literal needs the evaluator: the scalars, and
104	/// the collections whose every element is itself directly convertible. The
105	/// result is required to equal what evaluating the literal produces, so a
106	/// caller may use either interchangeably. In particular an object's entries
107	/// are ordered and a repeated key resolves to its last occurrence, matching
108	/// evaluation.
109	///
110	/// `None` for anything whose value is not a property of the literal alone:
111	/// a record id with a generated key (the key is allocated when the literal
112	/// is evaluated), and any collection containing such an element. `None` is
113	/// always a safe answer, so a caller must treat it as "ask the evaluator"
114	/// rather than as "not a value".
115	///
116	/// This is the shape a `.surql` import consists of almost entirely, and
117	/// answering it without the evaluator is what keeps import off the async
118	/// stack: see the callers in the legacy literal evaluator and in the
119	/// planner.
120	pub fn as_static_value(&self) -> Option<crate::val::Value> {
121		use crate::val::Value;
122
123		let value = match self {
124			Literal::None => Value::None,
125			Literal::Null => Value::Null,
126			Literal::UnboundedRange => Value::Range(Box::new(crate::val::Range::unbounded())),
127			Literal::Bool(x) => Value::Bool(*x),
128			Literal::Float(x) => Value::Number(crate::val::Number::Float(*x)),
129			Literal::Integer(x) => Value::Number(crate::val::Number::Int(*x)),
130			Literal::Decimal(x) => Value::Number(crate::val::Number::Decimal(*x)),
131			Literal::String(x) => Value::String(x.clone()),
132			Literal::Bytes(x) => Value::Bytes(x.clone()),
133			Literal::Regex(x) => Value::Regex(x.clone()),
134			Literal::Duration(x) => Value::Duration(*x),
135			Literal::Datetime(x) => Value::Datetime(*x),
136			Literal::Uuid(x) => Value::Uuid(*x),
137			Literal::Geometry(x) => Value::Geometry(x.clone()),
138			Literal::File(x) => Value::File(x.clone()),
139			// Only the key forms that are values in their own right. A
140			// generated key is allocated during evaluation, and the compound
141			// keys hold expressions.
142			Literal::RecordId(rid) => {
143				use crate::expr::RecordIdKeyLit;
144				let key = match &rid.key {
145					RecordIdKeyLit::Number(x) => crate::val::RecordIdKey::Number(*x),
146					RecordIdKeyLit::String(x) => crate::val::RecordIdKey::String(x.clone()),
147					RecordIdKeyLit::Uuid(x) => crate::val::RecordIdKey::Uuid(*x),
148					_ => return None,
149				};
150				Value::RecordId(crate::val::RecordId::new(rid.table.clone(), key))
151			}
152			Literal::Array(exprs) => {
153				let mut values = Vec::with_capacity(exprs.len());
154				for e in exprs {
155					values.push(e.as_static_value()?);
156				}
157				Value::Array(crate::val::Array(values))
158			}
159			Literal::Set(exprs) => {
160				let mut set = crate::val::Set::new();
161				for e in exprs {
162					set.insert(e.as_static_value()?);
163				}
164				Value::Set(set)
165			}
166			// Built through a sorted map, exactly as evaluation builds it, so
167			// key ordering and duplicate-key resolution cannot drift apart.
168			Literal::Object(items) => {
169				let mut map = std::collections::BTreeMap::new();
170				for i in items {
171					map.insert(i.key.clone(), i.value.as_static_value()?);
172				}
173				Value::Object(crate::val::Object::from(map))
174			}
175		};
176		Some(value)
177	}
178}
179
180impl PartialEq for Literal {
181	fn eq(&self, other: &Self) -> bool {
182		match (self, other) {
183			(Literal::None, Literal::None) => true,
184			(Literal::Null, Literal::Null) => true,
185			(Literal::UnboundedRange, Literal::UnboundedRange) => true,
186			(Literal::Bool(a), Literal::Bool(b)) => a == b,
187			(Literal::Float(a), Literal::Float(b)) => a.to_bits() == b.to_bits(),
188			(Literal::Integer(a), Literal::Integer(b)) => a == b,
189			(Literal::Decimal(a), Literal::Decimal(b)) => a == b,
190			(Literal::String(a), Literal::String(b)) => a == b,
191			(Literal::Bytes(a), Literal::Bytes(b)) => a == b,
192			(Literal::Regex(a), Literal::Regex(b)) => a == b,
193			(Literal::RecordId(a), Literal::RecordId(b)) => a == b,
194			(Literal::Array(a), Literal::Array(b)) => a == b,
195			(Literal::Set(a), Literal::Set(b)) => a == b,
196			(Literal::Object(a), Literal::Object(b)) => a == b,
197			(Literal::Duration(a), Literal::Duration(b)) => a == b,
198			(Literal::Datetime(a), Literal::Datetime(b)) => a == b,
199			(Literal::Uuid(a), Literal::Uuid(b)) => a == b,
200			(Literal::Geometry(a), Literal::Geometry(b)) => a == b,
201			(Literal::File(a), Literal::File(b)) => a == b,
202			// Every variant must appear above. This arm is for mismatched
203			// variants only: a variant missing from the list falls through to
204			// it and compares unequal to itself, which breaks the `Eq` asserted
205			// below and silently defeats every structural comparison of an
206			// expression tree containing it.
207			_ => false,
208		}
209	}
210}
211impl Eq for Literal {}
212
213impl Hash for Literal {
214	fn hash<H: Hasher>(&self, state: &mut H) {
215		std::mem::discriminant(self).hash(state);
216		match self {
217			Literal::None => {}
218			Literal::Null => {}
219			Literal::UnboundedRange => {}
220			Literal::Bool(x) => x.hash(state),
221			Literal::Float(x) => x.to_bits().hash(state),
222			Literal::Integer(x) => x.hash(state),
223			Literal::Decimal(x) => x.hash(state),
224			Literal::String(x) => x.hash(state),
225			Literal::Bytes(x) => x.hash(state),
226			Literal::Regex(x) => x.hash(state),
227			Literal::RecordId(x) => x.hash(state),
228			Literal::Array(x) => x.hash(state),
229			Literal::Set(x) => x.hash(state),
230			Literal::Object(x) => x.hash(state),
231			Literal::Duration(x) => x.hash(state),
232			Literal::Datetime(x) => x.hash(state),
233			Literal::Uuid(x) => x.hash(state),
234			Literal::Geometry(x) => x.hash(state),
235			Literal::File(x) => x.hash(state),
236		}
237	}
238}
239
240impl ToSql for Literal {
241	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
242		let lit: crate::sql::Literal = self.clone().into();
243		lit.fmt_sql(f, fmt);
244	}
245}
246
247#[derive(Clone, Debug, Eq, PartialEq, Hash)]
248pub struct ObjectEntry {
249	pub key: Strand,
250	pub value: Expr,
251}
252
253impl ToSql for ObjectEntry {
254	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
255		let entry: crate::sql::literal::ObjectEntry = self.clone().into();
256		entry.fmt_sql(f, fmt);
257	}
258}
259
260#[cfg(test)]
261mod equality_tests {
262	use rust_decimal::Decimal;
263
264	use super::Literal;
265	use crate::val::Strand;
266
267	/// `Eq` is asserted for `Literal`, so every variant must equal itself.
268	///
269	/// The impl is hand-written with a `_ => false` fallthrough, so a variant
270	/// omitted from it compares unequal to itself rather than failing to
271	/// compile. That breaks any structural comparison of an expression
272	/// containing it: a change-detector sees a spurious difference, and an
273	/// index-guard match sees none.
274	#[test]
275	fn every_variant_equals_itself() {
276		// The exhaustive match is the point: it makes adding a variant a
277		// compile error here, so the sample list cannot fall behind the enum
278		// the way a hand-maintained subset does.
279		fn _forces_this_list_to_be_updated(l: &Literal) {
280			match l {
281				Literal::None
282				| Literal::Null
283				| Literal::UnboundedRange
284				| Literal::Bool(_)
285				| Literal::Float(_)
286				| Literal::Integer(_)
287				| Literal::Decimal(_)
288				| Literal::String(_)
289				| Literal::Bytes(_)
290				| Literal::Regex(_)
291				| Literal::RecordId(_)
292				| Literal::Array(_)
293				| Literal::Set(_)
294				| Literal::Object(_)
295				| Literal::Duration(_)
296				| Literal::Datetime(_)
297				| Literal::Uuid(_)
298				| Literal::Geometry(_)
299				| Literal::File(_) => {}
300			}
301		}
302		let samples = [
303			Literal::None,
304			Literal::Null,
305			Literal::UnboundedRange,
306			Literal::Bool(true),
307			Literal::Float(1.5),
308			Literal::Float(-0.0),
309			Literal::Integer(7),
310			Literal::Decimal(Decimal::new(150, 2)),
311			Literal::String(Strand::new("s")),
312			Literal::Bytes(crate::val::Bytes::from(vec![1u8])),
313			Literal::Regex("a".parse::<crate::val::Regex>().unwrap()),
314			Literal::RecordId(crate::expr::RecordIdLit {
315				table: "t".into(),
316				key: crate::expr::RecordIdKeyLit::Number(1),
317			}),
318			Literal::Array(vec![]),
319			Literal::Set(vec![]),
320			Literal::Object(vec![]),
321			Literal::Duration(crate::val::Duration::from_secs(1)),
322			Literal::Datetime(crate::val::Datetime::MIN_UTC),
323			Literal::Uuid(crate::val::Uuid::nil()),
324			Literal::Geometry(crate::val::Geometry::Point(geo::Point::new(1.0, 2.0))),
325			Literal::File(crate::val::File::new("b".to_owned(), "p".to_owned())),
326		];
327		for s in samples {
328			assert_eq!(s, s.clone(), "{s:?} must equal itself");
329		}
330	}
331
332	/// `Float` compares by bit pattern, so the two zeroes are distinct — they
333	/// render differently (`-0f` vs `0f`), and treating them as equal would let
334	/// a rewrite that changes one into the other be dropped as a no-op.
335	#[test]
336	fn signed_zero_is_not_equal_to_zero() {
337		assert_ne!(Literal::Float(-0.0), Literal::Float(0.0));
338	}
339}
340
341#[cfg(test)]
342mod static_value_tests {
343	use surrealdb_strand::Strand;
344
345	use super::{Literal, ObjectEntry};
346	use crate::expr::record_id::RecordIdKeyGen;
347	use crate::expr::{Expr, RecordIdKeyLit, RecordIdLit};
348	use crate::val::Value;
349
350	fn entry(key: &str, value: Literal) -> ObjectEntry {
351		ObjectEntry {
352			key: Strand::new(key),
353			value: Expr::Literal(value),
354		}
355	}
356
357	/// An object's entries come out ordered by key regardless of source order,
358	/// because the value has to be indistinguishable from the evaluated one.
359	#[test]
360	fn object_entries_are_ordered_by_key() {
361		let lit =
362			Literal::Object(vec![entry("z", Literal::Integer(1)), entry("a", Literal::Integer(2))]);
363		let Some(Value::Object(obj)) = lit.as_static_value() else {
364			panic!("a literal object of literals is a value");
365		};
366		let keys: Vec<&str> = obj.iter().map(|(k, _)| k.as_str()).collect();
367		assert_eq!(keys, ["a", "z"]);
368	}
369
370	/// A repeated key resolves to its last occurrence, matching what inserting
371	/// each entry in source order produces.
372	#[test]
373	fn repeated_object_key_keeps_the_last() {
374		let lit =
375			Literal::Object(vec![entry("a", Literal::Integer(1)), entry("a", Literal::Integer(3))]);
376		let Some(Value::Object(obj)) = lit.as_static_value() else {
377			panic!("a literal object of literals is a value");
378		};
379		assert_eq!(obj.len(), 1);
380		assert_eq!(obj.get("a"), Some(&Value::Number(crate::val::Number::Int(3))));
381	}
382
383	/// A generated record-id key is allocated during evaluation, so a literal
384	/// carrying one is not a value on its own — and neither is any collection
385	/// holding it, or the fast path would hand out ids the evaluator never
386	/// allocated.
387	#[test]
388	fn generated_record_id_key_is_not_static() {
389		let lit = Literal::RecordId(RecordIdLit {
390			table: "t".into(),
391			key: RecordIdKeyLit::Generate(RecordIdKeyGen::Ulid),
392		});
393		assert!(lit.as_static_value().is_none());
394		let nested = Literal::Object(vec![entry("id", lit.clone())]);
395		assert!(nested.as_static_value().is_none());
396		let in_array = Literal::Array(vec![Expr::Literal(lit)]);
397		assert!(in_array.as_static_value().is_none());
398	}
399
400	/// A collection is a value only if every element is, so one non-literal
401	/// element withholds the whole collection from the fast path.
402	#[test]
403	fn non_literal_element_withholds_the_collection() {
404		let lit = Literal::Array(vec![
405			Expr::Literal(Literal::Integer(1)),
406			Expr::Idiom(crate::expr::Idiom::field("f".to_owned())),
407		]);
408		assert!(lit.as_static_value().is_none());
409	}
410
411	/// Simple record-id keys are values, which is what makes an exported
412	/// record's `id` field take the fast path.
413	#[test]
414	fn simple_record_id_keys_are_static() {
415		for key in [
416			RecordIdKeyLit::Number(1),
417			RecordIdKeyLit::String(Strand::new("a")),
418			RecordIdKeyLit::Uuid(crate::val::Uuid::nil()),
419		] {
420			let lit = Literal::RecordId(RecordIdLit {
421				table: "t".into(),
422				key,
423			});
424			assert!(matches!(lit.as_static_value(), Some(Value::RecordId(_))));
425		}
426	}
427}