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#[derive(Clone, Debug)]
26pub enum Literal {
27 None,
28 Null,
29 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 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 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 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 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 _ => 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 #[test]
275 fn every_variant_equals_itself() {
276 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 #[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 #[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 #[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 #[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 #[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 #[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}